From 3fa1279d2923632080c1de1805ed0b83b697dd2b Mon Sep 17 00:00:00 2001 From: Aruki Date: Tue, 4 Jul 2017 19:02:56 -0600 Subject: [PATCH] Lots of changes to how resource database/entry data is serialized; resource database file is now binary and merged with the cache data file. Binary reader/writer now use 32-bit sizes. --- resources/ResourceCacheData.bin | Bin 1344 -> 0 bytes resources/ResourceDatabase.xml | 505 ------------------ src/Common/Flags.h | 2 +- src/Common/Serialization/CBasicBinaryReader.h | 15 +- src/Common/Serialization/CBasicBinaryWriter.h | 26 +- src/Common/Serialization/CBinaryReader.h | 54 +- src/Common/Serialization/CBinaryWriter.h | 48 +- src/Common/Serialization/IArchive.h | 9 +- src/Core/GameProject/CGameExporter.cpp | 8 +- src/Core/GameProject/CGameProject.cpp | 4 +- src/Core/GameProject/CResourceEntry.cpp | 150 +++--- src/Core/GameProject/CResourceEntry.h | 18 +- src/Core/GameProject/CResourceStore.cpp | 264 ++++----- src/Core/GameProject/CResourceStore.h | 22 +- src/Editor/CEditorApplication.cpp | 2 +- src/Editor/CUIRelay.h | 10 +- src/Editor/main.cpp | 23 +- 17 files changed, 329 insertions(+), 831 deletions(-) delete mode 100644 resources/ResourceCacheData.bin delete mode 100644 resources/ResourceDatabase.xml diff --git a/resources/ResourceCacheData.bin b/resources/ResourceCacheData.bin deleted file mode 100644 index 905aa1cf3b22a6eb5c4f84b7b205ea3cbd3553b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1344 zcmZ>EboO8X0^fijUmz9C@pIA&Ak7gHEH+89? z@Gs4?M&=8~DBeZp%P?}?LFUWq3~xaOhM)wIjc*e z@K64{h|D)}2+Bw1TTLnKMCRLY8Uwiq|2j;YP=w5P?vz$V;lJFl5}EJftUd>s@1z_(h*c+JP(|%W>u#GC$s0L>if&xK;2eGC$2z zRTi0_CBC^InV&7c8pXcs!UHJo%gK5E4_SR~^ucr#{*B&PWPV=w_Xo)QqUpWok@=NU z)qj!s^`7o1>8a5uD;Qb6Dds*(dTq*?0hC6>SL->k>&X1}sC*Ru1j|Y(Wci8CB_hcD zNk1>5q~ED((kS6Qt;Zn_S^fMEUJH@=%PxlJBJ)>h*)t*Y*GNp|LgsI{s{b8@|6sy2 zWd0_50VNdv?Ngu(3D1w4S@|rG`8#VjWh3)<)h$6u9|wNFwMCXcq?R-ZnSbPk2}=4t zzVDwVvi#`@-YELdMW$~ - - - - - - 08F992A8 - TXTR - script/common/ - RandomRelay - - - 0A11104B - CMDL - editor/ - TranslateX - - - 0E04ED00 - TXTR - - LightAmbientMask - - - 0EDB1C7A - CMDL - script/dkcr/ - Waypoint - - - 0FB28A24 - TXTR - - LightAmbient - - - 0FD29E3B - CMDL - script/common/ - Waypoint - - - 115C21DD - TXTR - script/common/ - CameraFilterKeyframe - - - 1C010ADC - CMDL - editor/ - TranslatePolyXY - - - 1FFC7E43 - CMDL - editor/ - RotateXYZ - - - 2A14558D - TXTR - script/common/ - HUDMemo - - - 2B75E193 - TXTR - script/common/ - VisorFlare - - - 31485556 - TXTR - script/mp2/ - AdvancedCounter - - - 31992094 - CMDL - editor/ - TranslateY - - - 32432719 - CMDL - script/common/ - SpiderBallWaypoint - - - 32C9F9D1 - TXTR - script/common/ - StreamedAudio - - - 3440526F - CMDL - editor/ - TranslateLinesXY - - - 3A947189 - CMDL - script/common/ - AIWaypoint - - - 3C093300 - CMDL - editor/ - ScaleXYZ - - - 40969072 - TXTR - script/common/ - AreaAttributes - - - 43891B25 - CMDL - editor/ - TranslatePolyXZ - - - 43E9B0A9 - CMDL - editor/ - ScaleX - - - 4443279C - TXTR - script/common/ - CameraBlurKeyframe - - - 4A2D76D1 - TXTR - script/common/ - DamageableTrigger - - - 4C3D3E7F - TXTR - script/common/ - GrapplePoint - - - 4E133EF2 - TXTR - script/common/ - SpiderBallAttractionSurface - - - 57F8715F - TXTR - script/common/ - Dock - - - 592130DD - CMDL - editor/ - TranslateZ - - - 5BC862B8 - CMDL - editor/ - TranslateLinesXZ - - - 5D08CCF6 - TXTR - script/mp3/ - WeaponGenerator - - - 5F3B141B - CMDL - - VolumeBox - - - 61B511E5 - TXTR - - Checkerboard - - - 6649251D - CMDL - script/common/ - Camera - - - 6A17B38F - TXTR - script/common/ - Sound - - - 6B1FABDD - CMDL - - Cube - - - 6B71C0F2 - CMDL - editor/ - ScaleY - - - 6C6CE7FE - TXTR - - LightSpotMask - - - 6D5BC167 - TXTR - script/common/ - Effect - - - 6DD88D5D - CMDL - - RotationArrow - - - 6E57F7E0 - CMDL - - VolumeCylinder - - - 72978DCF - TXTR - script/common/ - Relay - - - 791A7BFD - CMDL - - SphereDoubleSided - - - 7F494724 - CMDL - editor/ - RotateX - - - 81326A53 - CMDL - editor/ - ScaleLinesYZ - - - 825CDFA8 - CMDL - editor/ - ScalePolyYZ - - - 826C9800 - TXTR - script/common/ - ColorModulate - - - 85CE16D7 - TXTR - script/common/ - StreamedMovie - - - 875A6FD7 - TXTR - script/common/ - PointOfInterest - - - 9039791A - TXTR - - LightDirectional - - - 91437414 - CMDL - script/common/ - CameraWaypoint - - - 92F9D13B - CMDL - editor/ - ScaleZ - - - 95261BB8 - CMDL - - WireSphere - - - 968C405E - TXTR - script/common/ - SoundModifier - - - 9FF04AA1 - TXTR - script/common/ - Generator - - - A6D1576D - CMDL - editor/ - RotateY - - - A82A3F02 - TXTR - - LightSpot - - - AC18950A - TXTR - script/common/ - RadialDamage - - - B0D52FF7 - TXTR - - LightCustomMask - - - B0E09096 - TXTR - - LightDirectionalMask - - - B23F1022 - TXTR - script/common/ - VisorGoo - - - B2DBCAED - TXTR - script/common/ - PickupGenerator - - - B3050E38 - TXTR - script/common/ - SequenceTimer - - - B97DB26B - TXTR - script/common/ - Timer - - - BA7EA4F6 - TXTR - script/common/ - Trigger - - - C0FBED3D - CMDL - editor/ - ScaleLinesXY - - - C2266292 - CMDL - editor/ - ScalePolyXY - - - C4E83425 - TXTR - script/common/ - DistanceFog - - - C7BEFE29 - CMDL - - VolumeSphere - - - CB904B3D - TXTR - script/common/ - SpecialFunction - - - CE5967B6 - CMDL - editor/ - RotateZ - - - D2F07DAF - TXTR - script/common/ - ConditionalRelay - - - D7B6A50D - CMDL - - Sphere - - - DA185196 - TXTR - - VolumeCheckerboard - - - DC3787F2 - CMDL - editor/ - TranslatePolyYZ - - - E1A0D860 - TXTR - script/common/ - StreamedAudioModifier - - - E883FD86 - CMDL - editor/ - ScaleLinesXZ - - - E9AE72DB - CMDL - editor/ - ScalePolyXZ - - - EE340FC4 - TXTR - script/common/ - MemoryRelay - - - F476CF85 - CMDL - editor/ - TranslateLinesYZ - - - F76B369A - TXTR - script/common/ - Counter - - - FB50DA78 - CMDL - editor/ - RotateClipOutline - - - FC8FA98B - TXTR - - LightCustom - - - FD78FBC8 - TXTR - script/mp1/ - NewCameraShaker - - - FFAC2525 - TXTR - script/common/ - CameraShaker - - - diff --git a/src/Common/Flags.h b/src/Common/Flags.h index 732b63a0..90b96b5a 100644 --- a/src/Common/Flags.h +++ b/src/Common/Flags.h @@ -7,7 +7,7 @@ template class TFlags { - int mValue; + u32 mValue; public: TFlags() : mValue(0) {} diff --git a/src/Common/Serialization/CBasicBinaryReader.h b/src/Common/Serialization/CBasicBinaryReader.h index 5bd849d4..6ebe724b 100644 --- a/src/Common/Serialization/CBasicBinaryReader.h +++ b/src/Common/Serialization/CBasicBinaryReader.h @@ -12,22 +12,27 @@ class CBasicBinaryReader : public IArchive { IInputStream *mpStream; + bool mMagicValid; bool mOwnsStream; public: - CBasicBinaryReader(const TString& rkFilename, IOUtil::EEndianness = IOUtil::eLittleEndian) + CBasicBinaryReader(const TString& rkFilename, u32 Magic) : IArchive(true, false) , mOwnsStream(true) { mpStream = new CFileInStream(rkFilename, IOUtil::eBigEndian); - ASSERT(mpStream->IsValid()); - CSerialVersion Version(*mpStream); - SetVersion(Version); + if (mpStream->IsValid()) + { + mMagicValid = (mpStream->ReadLong() == Magic); + CSerialVersion Version(*mpStream); + SetVersion(Version); + } } CBasicBinaryReader(IInputStream *pStream, const CSerialVersion& rkVersion) : IArchive(true, false) + , mMagicValid(true) , mOwnsStream(false) { ASSERT(pStream->IsValid()); @@ -40,6 +45,8 @@ public: if (mOwnsStream) delete mpStream; } + inline bool IsValid() const { return mpStream->IsValid(); } + // Interface virtual bool ParamBegin(const char*) { return true; } virtual void ParamEnd() { } diff --git a/src/Common/Serialization/CBasicBinaryWriter.h b/src/Common/Serialization/CBasicBinaryWriter.h index 30da25e6..fe518906 100644 --- a/src/Common/Serialization/CBasicBinaryWriter.h +++ b/src/Common/Serialization/CBasicBinaryWriter.h @@ -11,20 +11,26 @@ class CBasicBinaryWriter : public IArchive { IOutputStream *mpStream; + u32 mMagic; bool mOwnsStream; public: - CBasicBinaryWriter(const TString& rkFilename, u16 FileVersion, EGame Game = eUnknownGame, IOUtil::EEndianness = IOUtil::eLittleEndian) + CBasicBinaryWriter(const TString& rkFilename, u32 Magic, u16 FileVersion, EGame Game) : IArchive(false, true) + , mMagic(Magic) , mOwnsStream(true) { mpStream = new CFileOutStream(rkFilename, IOUtil::eBigEndian); - ASSERT(mpStream->IsValid()); - SetVersion(skCurrentArchiveVersion, FileVersion, Game); - GetVersionInfo().Write(*mpStream); + + if (mpStream->IsValid()) + { + mpStream->WriteLong(0); // Magic is written after the rest of the file is successfully saved + SetVersion(skCurrentArchiveVersion, FileVersion, Game); + GetVersionInfo().Write(*mpStream); + } } - CBasicBinaryWriter(IOutputStream *pStream, u16 FileVersion, EGame Game = eUnknownGame) + CBasicBinaryWriter(IOutputStream *pStream, u16 FileVersion, EGame Game) : IArchive(false, true) , mOwnsStream(false) { @@ -44,9 +50,17 @@ public: ~CBasicBinaryWriter() { - if (mOwnsStream) delete mpStream; + // Write magic and delete stream + if (mOwnsStream) + { + mpStream->GoTo(0); + mpStream->WriteLong(mMagic); + delete mpStream; + } } + inline bool IsValid() const { return mpStream->IsValid(); } + // Interface virtual bool ParamBegin(const char*) { return true; } virtual void ParamEnd() { } diff --git a/src/Common/Serialization/CBinaryReader.h b/src/Common/Serialization/CBinaryReader.h index 62ac2298..f224d1d8 100644 --- a/src/Common/Serialization/CBinaryReader.h +++ b/src/Common/Serialization/CBinaryReader.h @@ -10,35 +10,40 @@ class CBinaryReader : public IArchive struct SParameter { u32 Offset; - u16 Size; - u16 NumChildren; - u16 ChildIndex; + u32 Size; + u32 NumChildren; + u32 ChildIndex; bool Abstract; }; std::vector mParamStack; IInputStream *mpStream; + bool mMagicValid; bool mOwnsStream; public: - CBinaryReader(const TString& rkFilename) + CBinaryReader(const TString& rkFilename, u32 Magic) : IArchive(true, false) , mOwnsStream(true) { mpStream = new CFileInStream(rkFilename, IOUtil::eBigEndian); - ASSERT(mpStream->IsValid()); - CSerialVersion Version(*mpStream); - SetVersion(Version); + if (mpStream->IsValid()) + { + mMagicValid = (mpStream->ReadLong() == Magic); + CSerialVersion Version(*mpStream); + SetVersion(Version); + } InitParamStack(); } CBinaryReader(IInputStream *pStream, const CSerialVersion& rkVersion) : IArchive(true, false) + , mMagicValid(true) , mOwnsStream(false) { - ASSERT(pStream->IsValid()); + ASSERT(pStream && pStream->IsValid()); mpStream = pStream; SetVersion(rkVersion); @@ -50,25 +55,32 @@ public: if (mOwnsStream) delete mpStream; } + inline bool IsValid() const { return mpStream->IsValid() && mMagicValid; } + private: void InitParamStack() { mpStream->Skip(4); // Skip root ID (which is always -1) - u16 Size = mpStream->ReadShort(); + u32 Size = ReadSize(); u32 Offset = mpStream->Tell(); - u16 NumChildren = mpStream->ReadShort(); + u32 NumChildren = ReadSize(); mParamStack.push_back( SParameter { Offset, Size, NumChildren, 0, false } ); mParamStack.reserve(20); } public: // Interface + u32 ReadSize() + { + return (mArchiveVersion < eArVer_32BitBinarySize ? (u32) mpStream->ReadShort() : mpStream->ReadLong()); + } + virtual bool ParamBegin(const char *pkName) { // If this is the parent parameter's first child, then read the child count - if (mParamStack.back().NumChildren == 0xFFFF) + if (mParamStack.back().NumChildren == 0xFFFFFFFF) { - mParamStack.back().NumChildren = mpStream->ReadShort(); + mParamStack.back().NumChildren = ReadSize(); } // Save current offset @@ -79,12 +91,12 @@ public: if (mParamStack.back().ChildIndex < mParamStack.back().NumChildren) { u32 NextID = mpStream->ReadLong(); - u16 NextSize = mpStream->ReadShort(); + u32 NextSize = ReadSize(); // Does the next parameter ID match the current one? if (NextID == ParamID) { - mParamStack.push_back( SParameter { Offset, NextSize, 0xFFFF, 0, false } ); + mParamStack.push_back( SParameter { mpStream->Tell(), NextSize, 0xFFFFFFFF, 0, false } ); return true; } } @@ -94,20 +106,20 @@ public: { bool ParentAbstract = mParamStack.back().Abstract; u32 ParentOffset = mParamStack.back().Offset; - u16 NumChildren = mParamStack.back().NumChildren; - mpStream->GoTo(ParentOffset + (ParentAbstract ? 0xC : 0x8)); + u32 NumChildren = mParamStack.back().NumChildren; + mpStream->GoTo(ParentOffset + (ParentAbstract ? 4 : 0)); for (u32 ChildIdx = 0; ChildIdx < NumChildren; ChildIdx++) { u32 ChildID = mpStream->ReadLong(); - u16 ChildSize = mpStream->ReadShort(); + u32 ChildSize = ReadSize(); if (ChildID != ParamID) mpStream->Skip(ChildSize); else { - mParamStack.back().ChildIndex = (u16) ChildIdx; - mParamStack.push_back( SParameter { mpStream->Tell() - 6, ChildSize, 0xFFFF, 0, false } ); + mParamStack.back().ChildIndex = ChildIdx; + mParamStack.push_back( SParameter { mpStream->Tell(), ChildSize, 0xFFFFFFFF, 0, false } ); return true; } } @@ -122,7 +134,7 @@ public: { // Make sure we're at the end of the parameter SParameter& rParam = mParamStack.back(); - u32 EndOffset = rParam.Offset + rParam.Size + 6; + u32 EndOffset = rParam.Offset + rParam.Size; mpStream->GoTo(EndOffset); mParamStack.pop_back(); @@ -134,7 +146,7 @@ public: virtual void SerializeContainerSize(u32& rSize, const TString& /*rkElemName*/) { // Mostly handled by ParamBegin, we just need to return the size correctly so the container can be resized - rSize = (u32) mpStream->PeekShort(); + rSize = (mArchiveVersion < eArVer_32BitBinarySize ? (u32) mpStream->PeekShort() : mpStream->PeekLong()); } virtual void SerializeAbstractObjectType(u32& rType) diff --git a/src/Common/Serialization/CBinaryWriter.h b/src/Common/Serialization/CBinaryWriter.h index 796c4c63..78ee6fec 100644 --- a/src/Common/Serialization/CBinaryWriter.h +++ b/src/Common/Serialization/CBinaryWriter.h @@ -9,32 +9,39 @@ class CBinaryWriter : public IArchive struct SParameter { u32 Offset; - u16 NumSubParams; + u32 NumSubParams; bool Abstract; }; std::vector mParamStack; IOutputStream *mpStream; + u32 mMagic; bool mOwnsStream; public: - CBinaryWriter(const TString& rkFilename, u16 FileVersion, EGame Game = eUnknownGame) + CBinaryWriter(const TString& rkFilename, u32 Magic, u16 FileVersion, EGame Game) : IArchive(false, true) + , mMagic(Magic) , mOwnsStream(true) { mpStream = new CFileOutStream(rkFilename, IOUtil::eBigEndian); - ASSERT(mpStream->IsValid()); - SetVersion(skCurrentArchiveVersion, FileVersion, Game); - GetVersionInfo().Write(*mpStream); + if (mpStream->IsValid()) + { + mpStream->WriteLong(0); // Magic is written after the rest of the file has been successfully written + SetVersion(skCurrentArchiveVersion, FileVersion, Game); + GetVersionInfo().Write(*mpStream); + } + InitParamStack(); } - CBinaryWriter(IOutputStream *pStream, u16 FileVersion, EGame Game = eUnknownGame) + CBinaryWriter(IOutputStream *pStream, u16 FileVersion, EGame Game) : IArchive(false, true) + , mMagic(0) , mOwnsStream(false) { - ASSERT(pStream->IsValid()); + ASSERT(pStream && pStream->IsValid()); mpStream = pStream; SetVersion(skCurrentArchiveVersion, FileVersion, Game); InitParamStack(); @@ -42,9 +49,10 @@ public: CBinaryWriter(IOutputStream *pStream, const CSerialVersion& rkVersion) : IArchive(false, true) + , mMagic(0) , mOwnsStream(false) { - ASSERT(pStream->IsValid()); + ASSERT(pStream && pStream->IsValid()); mpStream = pStream; SetVersion(rkVersion); InitParamStack(); @@ -58,17 +66,23 @@ public: // Finish root param ParamEnd(); - // Delete stream + // Write magic and delete stream if (mOwnsStream) + { + mpStream->GoTo(0); + mpStream->WriteLong(mMagic); delete mpStream; + } } + inline bool IsValid() const { return mpStream->IsValid(); } + private: void InitParamStack() { mParamStack.reserve(20); mpStream->WriteLong(0xFFFFFFFF); - mpStream->WriteShort(0); // Size filler + mpStream->WriteLong(0); // Size filler mParamStack.push_back( SParameter { mpStream->Tell(), 0, false } ); } @@ -80,12 +94,12 @@ public: mParamStack.back().NumSubParams++; if (mParamStack.back().NumSubParams == 1) - mpStream->WriteShort(-1); // Sub-param count filler + mpStream->WriteLong(-1); // Sub-param count filler // Write param metadata u32 ParamID = TString(pkName).Hash32(); mpStream->WriteLong(ParamID); - mpStream->WriteShort((u16) 0xFFFF); // Param size filler + mpStream->WriteLong(-1); // Param size filler // Add new param to the stack mParamStack.push_back( SParameter { mpStream->Tell(), 0, false } ); @@ -99,16 +113,16 @@ public: SParameter& rParam = mParamStack.back(); u32 StartOffset = rParam.Offset; u32 EndOffset = mpStream->Tell(); - u16 ParamSize = (u16) (EndOffset - StartOffset); + u32 ParamSize = (EndOffset - StartOffset); - mpStream->GoTo(StartOffset - 2); - mpStream->WriteShort(ParamSize); + mpStream->GoTo(StartOffset - 4); + mpStream->WriteLong(ParamSize); // Write param child count if (rParam.NumSubParams > 0 || mParamStack.size() == 1) { if (rParam.Abstract) mpStream->Skip(4); - mpStream->WriteShort(rParam.NumSubParams); + mpStream->WriteLong(rParam.NumSubParams); } mpStream->GoTo(EndOffset); @@ -119,7 +133,7 @@ public: { // Normally handled by ParamBegin and ParamEnd but we need to do something here to account for zero-sized containers if (rSize == 0) - mpStream->WriteShort(0); + mpStream->WriteLong(0); } virtual void SerializeAbstractObjectType(u32& rType) diff --git a/src/Common/Serialization/IArchive.h b/src/Common/Serialization/IArchive.h index 18ec61ff..b7a871d3 100644 --- a/src/Common/Serialization/IArchive.h +++ b/src/Common/Serialization/IArchive.h @@ -178,7 +178,14 @@ protected: bool mIsWriter; public: - static const u32 skCurrentArchiveVersion = 0; + enum EArchiveVersion + { + eArVer_Initial, + eArVer_32BitBinarySize, + // Insert new versions before this line + eArVer_Max + }; + static const u32 skCurrentArchiveVersion = (eArVer_Max - 1); IArchive(bool IsReader, bool IsWriter) : mFileVersion(0) diff --git a/src/Core/GameProject/CGameExporter.cpp b/src/Core/GameProject/CGameExporter.cpp index 2875262e..91a90807 100644 --- a/src/Core/GameProject/CGameExporter.cpp +++ b/src/Core/GameProject/CGameExporter.cpp @@ -542,11 +542,11 @@ void CGameExporter::ExportResourceEditorData() // All resources should have dependencies generated, so save the project files SCOPED_TIMER(SaveResourceDatabase); #if EXPORT_COOKED - mpStore->SaveResourceDatabase(); + bool ResDBSaveSuccess = mpStore->SaveDatabaseCache(); + ASSERT(ResDBSaveSuccess); #endif - bool SaveSuccess = mpProject->Save(); - ASSERT(SaveSuccess); - mpStore->SaveCacheFile(); + bool ProjectSaveSuccess = mpProject->Save(); + ASSERT(ProjectSaveSuccess); } } diff --git a/src/Core/GameProject/CGameProject.cpp b/src/Core/GameProject/CGameProject.cpp index 74b69bbe..6ad73368 100644 --- a/src/Core/GameProject/CGameProject.cpp +++ b/src/Core/GameProject/CGameProject.cpp @@ -9,7 +9,7 @@ CGameProject::~CGameProject() { if (mpResourceStore) { - ASSERT(!mpResourceStore->IsDirty()); + ASSERT(!mpResourceStore->IsCacheDirty()); if (gpResourceStore == mpResourceStore) gpResourceStore = nullptr; @@ -238,7 +238,7 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti // Load resource database pProgress->Report("Loading resource database"); pProj->mpResourceStore = new CResourceStore(pProj); - LoadSuccess = pProj->mpResourceStore->LoadResourceDatabase(); + LoadSuccess = pProj->mpResourceStore->LoadDatabaseCache(); // Validate resource database if (LoadSuccess) diff --git a/src/Core/GameProject/CResourceEntry.cpp b/src/Core/GameProject/CResourceEntry.cpp index d355227a..fd4ef010 100644 --- a/src/Core/GameProject/CResourceEntry.cpp +++ b/src/Core/GameProject/CResourceEntry.cpp @@ -10,24 +10,70 @@ #include #include -CResourceEntry::CResourceEntry(CResourceStore *pStore, const CAssetID& rkID, - const TString& rkDir, const TString& rkFilename, - EResType Type) +CResourceEntry::CResourceEntry(CResourceStore *pStore) : mpResource(nullptr) + , mpTypeInfo(nullptr) , mpStore(pStore) , mpDependencies(nullptr) - , mID(rkID) + , mID( CAssetID::InvalidID(pStore->Game()) ) , mpDirectory(nullptr) - , mName(rkFilename) , mMetadataDirty(false) , mCachedSize(-1) - , mCachedUppercaseName(rkFilename.ToUpper()) -{ - mpTypeInfo = CResTypeInfo::FindTypeInfo(Type); - ASSERT(mpTypeInfo); +{} - mpDirectory = mpStore->GetVirtualDirectory(rkDir, true); - if (mpDirectory) mpDirectory->AddChild("", this); +// Static constructors +CResourceEntry* CResourceEntry::CreateNewResource(CResourceStore *pStore, const CAssetID& rkID, + const TString& rkDir, const TString& rkName, + EResType Type) +{ + // Initialize all entry info with the input data. + CResourceEntry *pEntry = new CResourceEntry(pStore); + pEntry->mID = rkID; + pEntry->mName = rkName; + pEntry->mCachedUppercaseName = rkName.ToUpper(); + + pEntry->mpTypeInfo = CResTypeInfo::FindTypeInfo(Type); + ASSERT(pEntry->mpTypeInfo); + + pEntry->mpDirectory = pStore->GetVirtualDirectory(rkDir, true); + ASSERT(pEntry->mpDirectory); + pEntry->mpDirectory->AddChild("", pEntry); + + pEntry->mMetadataDirty = true; + return pEntry; +} + +CResourceEntry* CResourceEntry::BuildFromArchive(CResourceStore *pStore, IArchive& rArc) +{ + // Load all entry info from the archive. + CResourceEntry *pEntry = new CResourceEntry(pStore); + pEntry->SerializeEntryInfo(rArc, false); + ASSERT(pEntry->mpTypeInfo); + ASSERT(pEntry->mpDirectory); + return pEntry; +} + +CResourceEntry* CResourceEntry::BuildFromDirectory(CResourceStore *pStore, CResTypeInfo *pTypeInfo, + const TString& rkDirPath, const TString& rkName) +{ + // Initialize as much entry info as possible from the input data, then load the rest from the metadata file. + ASSERT(pTypeInfo); + + CResourceEntry *pEntry = new CResourceEntry(pStore); + pEntry->mpTypeInfo = pTypeInfo; + pEntry->mName = rkName; + pEntry->mCachedUppercaseName = rkName.ToUpper(); + + pEntry->mpDirectory = pStore->GetVirtualDirectory(rkDirPath, true); + ASSERT(pEntry->mpDirectory); + pEntry->mpDirectory->AddChild("", pEntry); + + // Make sure we're valid, then load the remaining data from the metadata file + ASSERT(pEntry->HasCookedVersion() || pEntry->HasRawVersion()); + bool Success = pEntry->LoadMetadata(); + ASSERT(Success); + + return pEntry; } CResourceEntry::~CResourceEntry() @@ -39,25 +85,18 @@ CResourceEntry::~CResourceEntry() bool CResourceEntry::LoadMetadata() { ASSERT(!mMetadataDirty); + TString Path = MetadataFilePath(); + CBinaryReader MetaFile(Path, FOURCC('META')); - if (FileUtil::Exists(Path)) + if (MetaFile.IsValid()) { - // Validate file - CFileInStream MetaFile(Path, IOUtil::eBigEndian); - u32 Magic = MetaFile.ReadLong(); - - if (Magic == FOURCC('META')) - { - CSerialVersion Version(MetaFile); - CBinaryReader Reader(&MetaFile, Version); - SerializeMetadata(Reader); - return true; - } - else - { - Log::Error(Path + ": Failed to load metadata file, invalid magic: " + CFourCC(Magic).ToString()); - } + SerializeEntryInfo(MetaFile, true); + return true; + } + else + { + Log::Error(Path + ": Failed to load metadata file!"); } return false; @@ -71,24 +110,11 @@ bool CResourceEntry::SaveMetadata(bool ForceSave /*= false*/) TString Dir = Path.GetFileDirectory(); FileUtil::MakeDirectory(Dir); - CFileOutStream MetaFile(Path, IOUtil::eBigEndian); + CBinaryWriter MetaFile(Path, FOURCC('META'), 0, Game()); if (MetaFile.IsValid()) { - MetaFile.WriteLong(0); // Magic dummy - - CSerialVersion Version(IArchive::skCurrentArchiveVersion, 0, Game()); - Version.Write(MetaFile); - - // Scope the binary writer to ensure it finishes before we go back to write the magic value - { - CBinaryWriter Writer(&MetaFile, Version); - SerializeMetadata(Writer); - } - - MetaFile.GoTo(0); - MetaFile.WriteLong(FOURCC('META')); - + SerializeEntryInfo(MetaFile, true); mMetadataDirty = false; return true; } @@ -97,30 +123,34 @@ bool CResourceEntry::SaveMetadata(bool ForceSave /*= false*/) return false; } -void CResourceEntry::SerializeMetadata(IArchive& rArc) +void CResourceEntry::SerializeEntryInfo(IArchive& rArc, bool MetadataOnly) { - // Serialize ID. If we already have a valid ID then don't allow the file to override it. CAssetID ID = mID; - rArc << SERIAL("AssetID", ID); + rArc << SERIAL("AssetID", ID) + << SERIAL("Type", mpTypeInfo) + << SERIAL("Flags", mFlags); + + // Don't allow the file to override our asset ID if we already have a valid one. if (rArc.IsReader() && !mID.IsValid()) mID = ID; - // Serialize type - rArc << SERIAL("Type", mpTypeInfo); + // Serialize extra data that we exclude from the metadata file + if (!MetadataOnly) + { + TString Dir = (mpDirectory ? mpDirectory->FullPath() : ""); - // Serialize flags - u32 Flags = mFlags & eREF_SavedFlags; - rArc << SERIAL_AUTO(Flags); - if (rArc.IsReader()) mFlags = Flags & eREF_SavedFlags; -} + rArc << SERIAL("Name", mName) + << SERIAL("Directory", Dir) + << SERIAL_ABSTRACT("Dependencies", mpDependencies, &gDependencyNodeFactory); -void CResourceEntry::SerializeCacheData(IArchive& rArc) -{ - // Note: If the dependency tree format is changed this should be adjusted so that - // we regenerate the dependencies from scratch instead of reading the tree if the - // file version number is too low - rArc << SERIAL_ABSTRACT("Dependencies", mpDependencies, &gDependencyNodeFactory); + if (rArc.IsReader()) + { + mpDirectory = mpStore->GetVirtualDirectory(Dir, true); + mpDirectory->AddChild("", this); + mCachedUppercaseName = mName.ToUpper(); + } + } } void CResourceEntry::UpdateDependencies() @@ -150,7 +180,7 @@ void CResourceEntry::UpdateDependencies() } mpDependencies = mpResource->BuildDependencyTree(); - mpStore->SetCacheDataDirty(); + mpStore->SetCacheDirty(); if (!WasLoaded) mpStore->DestroyUnreferencedResources(); @@ -550,7 +580,7 @@ bool CResourceEntry::Move(const TString& rkDir, const TString& rkName, bool IsAu SetFlagEnabled(eREF_AutoResName, IsAutoGenName); } - mpStore->SetDatabaseDirty(); + mpStore->SetCacheDirty(); mCachedUppercaseName = rkName.ToUpper(); FileUtil::DeleteFile(OldRawPath); FileUtil::DeleteFile(OldCookedPath); diff --git a/src/Core/GameProject/CResourceEntry.h b/src/Core/GameProject/CResourceEntry.h index f642f05a..bb4e7e1c 100644 --- a/src/Core/GameProject/CResourceEntry.h +++ b/src/Core/GameProject/CResourceEntry.h @@ -22,9 +22,6 @@ enum EResEntryFlag eREF_HasBeenModified = 0x00000008, // Resource has been modified and resaved by the user eREF_AutoResName = 0x00000010, // Resource name is auto-generated eREF_AutoResDir = 0x00000020, // Resource directory name is auto-generated - // Flags that save to the cache file - eREF_SavedFlags = eREF_NeedsRecook | eREF_IsBaseGameResource | eREF_Hidden | eREF_HasBeenModified | - eREF_AutoResName | eREF_AutoResDir }; DECLARE_FLAGS(EResEntryFlag, FResEntryFlags) @@ -43,16 +40,21 @@ class CResourceEntry mutable u64 mCachedSize; mutable TString mCachedUppercaseName; // This is used to speed up case-insensitive sorting and filtering. + // Private constructor + CResourceEntry(CResourceStore *pStore); + public: - CResourceEntry(CResourceStore *pStore, const CAssetID& rkID, - const TString& rkDir, const TString& rkFilename, - EResType Type); + static CResourceEntry* CreateNewResource(CResourceStore *pStore, const CAssetID& rkID, + const TString& rkDir, const TString& rkName, + EResType Type); + static CResourceEntry* BuildFromArchive(CResourceStore *pStore, IArchive& rArc); + static CResourceEntry* BuildFromDirectory(CResourceStore *pStore, CResTypeInfo *pTypeInfo, + const TString& rkDirPath, const TString& rkName); ~CResourceEntry(); bool LoadMetadata(); bool SaveMetadata(bool ForceSave = false); - void SerializeMetadata(IArchive& rArc); - void SerializeCacheData(IArchive& rArc); + void SerializeEntryInfo(IArchive& rArc, bool MetadataOnly); void UpdateDependencies(); bool HasRawVersion() const; diff --git a/src/Core/GameProject/CResourceStore.cpp b/src/Core/GameProject/CResourceStore.cpp index 2c60d6ba..623e9b30 100644 --- a/src/Core/GameProject/CResourceStore.cpp +++ b/src/Core/GameProject/CResourceStore.cpp @@ -2,6 +2,7 @@ #include "CGameExporter.h" #include "CGameProject.h" #include "CResourceIterator.h" +#include "Core/IUIRelay.h" #include "Core/Resource/CResource.h" #include #include @@ -17,13 +18,12 @@ CResourceStore *gpEditorStore = nullptr; // Constructor for editor store CResourceStore::CResourceStore(const TString& rkDatabasePath) : mpProj(nullptr) - , mGame(eUnknownGame) - , mDatabaseDirty(false) - , mCacheFileDirty(false) + , mGame(ePrime) + , mDatabaseCacheDirty(false) { mpDatabaseRoot = new CVirtualDirectory(this); mDatabasePath = FileUtil::MakeAbsolute(rkDatabasePath.GetFileDirectory()); - mDatabaseName = rkDatabasePath.GetFileName(); + LoadDatabaseCache(); } // Main constructor for game projects and game exporter @@ -31,8 +31,7 @@ CResourceStore::CResourceStore(CGameProject *pProject) : mpProj(nullptr) , mGame(eUnknownGame) , mpDatabaseRoot(nullptr) - , mDatabaseDirty(false) - , mCacheFileDirty(false) + , mDatabaseCacheDirty(false) { SetProject(pProject); } @@ -60,58 +59,60 @@ void RecursiveGetListOfEmptyDirectories(CVirtualDirectory *pDir, TStringList& rO } } -bool CResourceStore::SerializeResourceDatabase(IArchive& rArc) +bool CResourceStore::SerializeDatabaseCache(IArchive& rArc) { - struct SDatabaseResource + // Serialize resources + if (rArc.ParamBegin("Resources")) { - CAssetID ID; - CResTypeInfo *pType; - TString Directory; - TString Name; + // Serialize resources + u32 ResourceCount = mResourceEntries.size(); + rArc << SERIAL_AUTO(ResourceCount); - void Serialize(IArchive& rArc) + if (rArc.IsReader()) { - rArc << SERIAL_AUTO(ID) << SERIAL("Type", pType) << SERIAL_AUTO(Directory) << SERIAL_AUTO(Name); + for (u32 ResIdx = 0; ResIdx < ResourceCount; ResIdx++) + { + if (rArc.ParamBegin("Resource")) + { + CResourceEntry *pEntry = CResourceEntry::BuildFromArchive(this, rArc); + ASSERT( FindEntry(pEntry->ID()) == nullptr ); + mResourceEntries[pEntry->ID()] = pEntry; + rArc.ParamEnd(); + } + } } - }; - std::vector Resources; - - // Populate resource list - if (!rArc.IsReader()) - { - Resources.reserve(mResourceEntries.size()); - - for (CResourceIterator It(this); It; ++It) - Resources.push_back( SDatabaseResource { It->ID(), It->TypeInfo(), It->Directory()->FullPath(), It->Name() } ); + else + { + for (CResourceIterator It(this); It; ++It) + { + if (rArc.ParamBegin("Resource")) + { + It->SerializeEntryInfo(rArc, false); + rArc.ParamEnd(); + } + } + } + rArc.ParamEnd(); } - // Populate directory list + // Serialize empty directory list TStringList EmptyDirectories; if (!rArc.IsReader()) RecursiveGetListOfEmptyDirectories(mpDatabaseRoot, EmptyDirectories); - // Serialize - rArc << SERIAL_CONTAINER_AUTO(Resources, "Resource") - << SERIAL_CONTAINER_AUTO(EmptyDirectories, "Directory"); + rArc << SERIAL_CONTAINER_AUTO(EmptyDirectories, "Directory"); - // Register resources if (rArc.IsReader()) { for (auto Iter = EmptyDirectories.begin(); Iter != EmptyDirectories.end(); Iter++) CreateVirtualDirectory(*Iter); - - for (auto Iter = Resources.begin(); Iter != Resources.end(); Iter++) - { - SDatabaseResource& rRes = *Iter; - RegisterResource(rRes.ID, rRes.pType->Type(), rRes.Directory, rRes.Name); - } } return true; } -bool CResourceStore::LoadResourceDatabase() +bool CResourceStore::LoadDatabaseCache() { ASSERT(!mDatabasePath.IsEmpty()); TString Path = DatabasePath(); @@ -119,139 +120,46 @@ bool CResourceStore::LoadResourceDatabase() if (!mpDatabaseRoot) mpDatabaseRoot = new CVirtualDirectory(this); - CXMLReader Reader(Path); + // Load the resource database + CBinaryReader Reader(Path, FOURCC('CACH')); - if (!Reader.IsValid()) + if (!Reader.IsValid() || !SerializeDatabaseCache(Reader)) { - Log::Error("Failed to open resource database for load: " + Path); - return false; + if (gpUIRelay->AskYesNoQuestion("Error", "Failed to load the resource database. Attempt to build from the directory? (This may take a while.)")) + { + if (!BuildFromDirectory(true)) + return false; + } + else return false; + } + else + { + // Database is succesfully loaded at this point + if (mpProj) + ASSERT(mpProj->Game() == Reader.Game()); } - - if (mpProj) - ASSERT(mpProj->Game() == Reader.Game()); mGame = Reader.Game(); - if (!SerializeResourceDatabase(Reader)) return false; - return LoadCacheFile(); -} - -bool CResourceStore::SaveResourceDatabase() -{ - TString Path = DatabasePath(); - CXMLWriter Writer(Path, "ResourceDB", 0, mGame); - SerializeResourceDatabase(Writer); - bool SaveSuccess = Writer.Save(); - - if (SaveSuccess) - mDatabaseDirty = false; - else - Log::Error("Failed to save resource database: " + Path); - - return SaveSuccess; -} - -bool CResourceStore::LoadCacheFile() -{ - TString CachePath = CacheDataPath(); - CFileInStream CacheFile(CachePath, IOUtil::eBigEndian); - - if (!CacheFile.IsValid()) - { - Log::Error("Failed to open cache file for load: " + CachePath); - return false; - } - - // Cache header - CFourCC Magic(CacheFile); - - if (Magic != FOURCC('CACH')) - { - Log::Error("Invalid resource cache data magic: " + Magic.ToString()); - return false; - } - - CSerialVersion Version(CacheFile); - u32 NumResources = CacheFile.ReadLong(); - - for (u32 iRes = 0; iRes < NumResources; iRes++) - { - CAssetID ID(CacheFile, Version.Game()); - u32 EntryCacheSize = CacheFile.ReadLong(); - u32 EntryCacheEnd = CacheFile.Tell() + EntryCacheSize; - - CResourceEntry *pEntry = FindEntry(ID); - - if (pEntry) - { - CBasicBinaryReader Reader(&CacheFile, Version); - - if (Reader.ParamBegin("EntryCache")) - { - pEntry->SerializeCacheData(Reader); - Reader.ParamEnd(); - } - } - - CacheFile.Seek(EntryCacheEnd, SEEK_SET); - } return true; } -bool CResourceStore::SaveCacheFile() +bool CResourceStore::SaveDatabaseCache() { - TString CachePath = CacheDataPath(); - CFileOutStream CacheFile(CachePath, IOUtil::eBigEndian); + TString Path = DatabasePath(); - if (!CacheFile.IsValid()) - { - Log::Error("Failed to open cache file for save: " + CachePath); + CBinaryWriter Writer(Path, FOURCC('CACH'), 0, mGame); + + if (!Writer.IsValid()) return false; - } - // Cache header - CacheFile.WriteLong(0); // Magic dummy. Magic isn't written until the rest of the file is saved successfully. - CSerialVersion Version(IArchive::skCurrentArchiveVersion, 0, mGame); - Version.Write(CacheFile); - - u32 ResCountOffset = CacheFile.Tell(); - u32 ResCount = 0; - CacheFile.WriteLong(0); // Resource count dummy - fill in when we know the real count - - // Save entry cache data - // Structure: Entry Asset ID -> Entry Cache Size -> Serialized Entry Cache Data - for (CResourceIterator It(this); It; ++It) - { - ResCount++; - It->ID().Write(CacheFile); - u32 SizeOffset = CacheFile.Tell(); - CacheFile.WriteLong(0); - - CBasicBinaryWriter Writer(&CacheFile, Version.FileVersion(), Version.Game()); - - if (Writer.ParamBegin("EntryCache")) - { - It->SerializeCacheData(Writer); - Writer.ParamEnd(); - } - - u32 EntryCacheEnd = CacheFile.Tell(); - CacheFile.Seek(SizeOffset, SEEK_SET); - CacheFile.WriteLong(EntryCacheEnd - SizeOffset - 4); - CacheFile.Seek(EntryCacheEnd, SEEK_SET); - } - - CacheFile.Seek(ResCountOffset, SEEK_SET); - CacheFile.WriteLong(ResCount); - CacheFile.Seek(0, SEEK_SET); - CacheFile.WriteLong( FOURCC('CACH') ); - mCacheFileDirty = false; + SerializeDatabaseCache(Writer); + mDatabaseCacheDirty = false; return true; } void CResourceStore::ConditionalSaveStore() { - if (mDatabaseDirty) SaveResourceDatabase(); - if (mCacheFileDirty) SaveCacheFile(); + if (mDatabaseCacheDirty) SaveDatabaseCache(); } void CResourceStore::SetProject(CGameProject *pProj) @@ -267,7 +175,6 @@ void CResourceStore::SetProject(CGameProject *pProj) { TString DatabasePath = mpProj->ResourceDBPath(false); mDatabasePath = DatabasePath.GetFileDirectory(); - mDatabaseName = DatabasePath.GetFileName(); mpDatabaseRoot = new CVirtualDirectory(this); mGame = mpProj->Game(); } @@ -377,13 +284,11 @@ void CResourceStore::ClearDatabase() delete mpDatabaseRoot; mpDatabaseRoot = new CVirtualDirectory(this); - mDatabaseDirty = true; - mCacheFileDirty = true; + mDatabaseCacheDirty = true; } -void CResourceStore::BuildFromDirectory() +bool CResourceStore::BuildFromDirectory(bool ShouldGenerateCacheFile) { - ASSERT(mpProj != nullptr); ASSERT(mResourceEntries.empty()); // Get list of resources @@ -415,8 +320,7 @@ void CResourceStore::BuildFromDirectory() } // Create resource entry - CResourceEntry *pEntry = new CResourceEntry(this, CAssetID::InvalidID(mGame), DirPath, ResName, pTypeInfo->Type()); - pEntry->LoadMetadata(); + CResourceEntry *pEntry = CResourceEntry::BuildFromDirectory(this, pTypeInfo, DirPath, ResName); // Validate the entry CAssetID ID = pEntry->ID(); @@ -429,6 +333,31 @@ void CResourceStore::BuildFromDirectory() else if (FileUtil::IsDirectory(Path)) CreateVirtualDirectory(RelPath); } + + // Generate new cache file + if (ShouldGenerateCacheFile) + { + // Make sure gpResourceStore points to this store + CResourceStore *pOldStore = gpResourceStore; + gpResourceStore = this; + + // Make sure audio manager is loaded correctly so AGSC dependencies can be looked up + if (mpProj) + mpProj->AudioManager()->LoadAssets(); + + // Update dependencies + for (CResourceIterator It(this); It; ++It) + It->UpdateDependencies(); + + // Update database file + mDatabaseCacheDirty = true; + ConditionalSaveStore(); + + // Restore old gpResourceStore + gpResourceStore = pOldStore; + } + + return true; } void CResourceStore::RebuildFromDirectory() @@ -436,19 +365,7 @@ void CResourceStore::RebuildFromDirectory() ASSERT(mpProj != nullptr); mpProj->AudioManager()->ClearAssets(); ClearDatabase(); - BuildFromDirectory(); - - // Make sure audio manager is loaded correctly so AGSC dependencies can be looked up - mpProj->AudioManager()->LoadAssets(); - - // Update dependencies - for (CResourceIterator It(this); It; ++It) - It->UpdateDependencies(); - - // Update database files - mDatabaseDirty = true; - mCacheFileDirty = true; - ConditionalSaveStore(); + BuildFromDirectory(true); } bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const @@ -468,8 +385,7 @@ CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType // Validate directory if (IsValidResourcePath(rkDir, rkName)) { - pEntry = new CResourceEntry(this, rkID, rkDir, rkName, Type); - pEntry->LoadMetadata(); + pEntry = CResourceEntry::CreateNewResource(this, rkID, rkDir, rkName, Type); mResourceEntries[rkID] = pEntry; } diff --git a/src/Core/GameProject/CResourceStore.h b/src/Core/GameProject/CResourceStore.h index 50507015..9cad0ee1 100644 --- a/src/Core/GameProject/CResourceStore.h +++ b/src/Core/GameProject/CResourceStore.h @@ -24,12 +24,10 @@ class CResourceStore CVirtualDirectory *mpDatabaseRoot; std::map mResourceEntries; std::map mLoadedResources; - bool mDatabaseDirty; - bool mCacheFileDirty; + bool mDatabaseCacheDirty; // Directory paths TString mDatabasePath; - TString mDatabaseName; enum EDatabaseVersion { @@ -43,11 +41,9 @@ public: CResourceStore(const TString& rkDatabasePath); CResourceStore(CGameProject *pProject); ~CResourceStore(); - bool SerializeResourceDatabase(IArchive& rArc); - bool LoadResourceDatabase(); - bool SaveResourceDatabase(); - bool LoadCacheFile(); - bool SaveCacheFile(); + bool SerializeDatabaseCache(IArchive& rArc); + bool LoadDatabaseCache(); + bool SaveDatabaseCache(); void ConditionalSaveStore(); void SetProject(CGameProject *pProj); void CloseProject(); @@ -61,7 +57,7 @@ public: CResourceEntry* FindEntry(const TString& rkPath) const; bool AreAllEntriesValid() const; void ClearDatabase(); - void BuildFromDirectory(); + bool BuildFromDirectory(bool ShouldGenerateCacheFile); void RebuildFromDirectory(); template ResType* LoadResource(const CAssetID& rkID) { return static_cast(LoadResource(rkID, ResType::StaticType())); } @@ -81,15 +77,13 @@ public: inline EGame Game() const { return mGame; } inline TString DatabaseRootPath() const { return mDatabasePath; } inline TString ResourcesDir() const { return IsEditorStore() ? DatabaseRootPath() : DatabaseRootPath() + "Resources/"; } - inline TString DatabasePath() const { return DatabaseRootPath() + "ResourceDatabase.xml"; } - inline TString CacheDataPath() const { return DatabaseRootPath() + "ResourceCacheData.bin"; } + inline TString DatabasePath() const { return DatabaseRootPath() + "ResourceDatabaseCache.bin"; } 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 bool IsCacheDirty() const { return mDatabaseCacheDirty; } - inline void SetDatabaseDirty() { mDatabaseDirty = true; } - inline void SetCacheDataDirty() { mCacheFileDirty = true; } + inline void SetCacheDirty() { mDatabaseCacheDirty = true; } inline bool IsEditorStore() const { return mpProj == nullptr; } }; diff --git a/src/Editor/CEditorApplication.cpp b/src/Editor/CEditorApplication.cpp index c41c9b7f..199b3e5b 100644 --- a/src/Editor/CEditorApplication.cpp +++ b/src/Editor/CEditorApplication.cpp @@ -253,7 +253,7 @@ void CEditorApplication::TickEditors() 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()) + if (gpResourceStore && gpResourceStore->IsCacheDirty()) { Log::Error("Resource store is dirty at the beginning of a tick! Call ConditionalSaveStore() after making any significant changes to assets!"); DEBUG_BREAK; diff --git a/src/Editor/CUIRelay.h b/src/Editor/CUIRelay.h index ade0c451..d94ec70c 100644 --- a/src/Editor/CUIRelay.h +++ b/src/Editor/CUIRelay.h @@ -6,10 +6,18 @@ #include "WorldEditor/CWorldEditor.h" #include "UICommon.h" +#include + class CUIRelay : public QObject, public IUIRelay { Q_OBJECT + Qt::ConnectionType GetConnectionType() + { + bool IsUIThread = (QThread::currentThread() == gpEdApp->thread()); + return IsUIThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection; + } + public: explicit CUIRelay(QObject *pParent = 0) : QObject(pParent) @@ -20,7 +28,7 @@ public: virtual bool AskYesNoQuestion(const TString& rkInfoBoxTitle, const TString& rkQuestion) { bool RetVal; - QMetaObject::invokeMethod(this, "AskYesNoQuestionSlot", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(this, "AskYesNoQuestionSlot", GetConnectionType(), Q_RETURN_ARG(bool, RetVal), Q_ARG(QString, TO_QSTRING(rkInfoBoxTitle)), Q_ARG(QString, TO_QSTRING(rkQuestion)) ); diff --git a/src/Editor/main.cpp b/src/Editor/main.cpp index 2bd02103..64ebdb29 100644 --- a/src/Editor/main.cpp +++ b/src/Editor/main.cpp @@ -32,22 +32,10 @@ int main(int argc, char *argv[]) App.setOrganizationName("Aruki"); App.setWindowIcon(QIcon(":/icons/AppIcon.ico")); - // Init log - bool Initialized = Log::InitLog("primeworldeditor.log"); - if (!Initialized) QMessageBox::warning(0, "Error", "Couldn't open log file. Logging will not work for this session."); - qInstallMessageHandler(QtLogRedirect); - // Create UI relay CUIRelay UIRelay(&App); gpUIRelay = &UIRelay; - // Create editor resource store - gpEditorStore = new CResourceStore("../resources/EditorResourceDB.rdb"); - gpEditorStore->LoadResourceDatabase(); - - // Load templates - CTemplateLoader::LoadGameList(); - // Set up dark theme qApp->setStyle(QStyleFactory::create("Fusion")); QPalette DarkPalette; @@ -66,6 +54,17 @@ int main(int argc, char *argv[]) DarkPalette.setColor(QPalette::HighlightedText, Qt::white); qApp->setPalette(DarkPalette); + // Init log + bool Initialized = Log::InitLog("primeworldeditor.log"); + if (!Initialized) QMessageBox::warning(0, "Error", "Couldn't open log file. Logging will not work for this session."); + qInstallMessageHandler(QtLogRedirect); + + // Create editor resource store + gpEditorStore = new CResourceStore("../resources/"); + + // Load templates + CTemplateLoader::LoadGameList(); + // Execute application App.InitEditor(); return App.exec();