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.

This commit is contained in:
Aruki 2017-07-04 19:02:56 -06:00
parent 4652e125e5
commit 3fa1279d29
17 changed files with 329 additions and 831 deletions

Binary file not shown.

View File

@ -1,505 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDB ArchiveVer="0" FileVer="0" Game="MPRM">
<RawDir></RawDir>
<CookedDir></CookedDir>
<Resources Size="83">
<Resource>
<ID>08F992A8</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>RandomRelay</Name>
</Resource>
<Resource>
<ID>0A11104B</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslateX</Name>
</Resource>
<Resource>
<ID>0E04ED00</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightAmbientMask</Name>
</Resource>
<Resource>
<ID>0EDB1C7A</ID>
<Type>CMDL</Type>
<Directory>script/dkcr/</Directory>
<Name>Waypoint</Name>
</Resource>
<Resource>
<ID>0FB28A24</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightAmbient</Name>
</Resource>
<Resource>
<ID>0FD29E3B</ID>
<Type>CMDL</Type>
<Directory>script/common/</Directory>
<Name>Waypoint</Name>
</Resource>
<Resource>
<ID>115C21DD</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>CameraFilterKeyframe</Name>
</Resource>
<Resource>
<ID>1C010ADC</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslatePolyXY</Name>
</Resource>
<Resource>
<ID>1FFC7E43</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>RotateXYZ</Name>
</Resource>
<Resource>
<ID>2A14558D</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>HUDMemo</Name>
</Resource>
<Resource>
<ID>2B75E193</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>VisorFlare</Name>
</Resource>
<Resource>
<ID>31485556</ID>
<Type>TXTR</Type>
<Directory>script/mp2/</Directory>
<Name>AdvancedCounter</Name>
</Resource>
<Resource>
<ID>31992094</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslateY</Name>
</Resource>
<Resource>
<ID>32432719</ID>
<Type>CMDL</Type>
<Directory>script/common/</Directory>
<Name>SpiderBallWaypoint</Name>
</Resource>
<Resource>
<ID>32C9F9D1</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>StreamedAudio</Name>
</Resource>
<Resource>
<ID>3440526F</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslateLinesXY</Name>
</Resource>
<Resource>
<ID>3A947189</ID>
<Type>CMDL</Type>
<Directory>script/common/</Directory>
<Name>AIWaypoint</Name>
</Resource>
<Resource>
<ID>3C093300</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScaleXYZ</Name>
</Resource>
<Resource>
<ID>40969072</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>AreaAttributes</Name>
</Resource>
<Resource>
<ID>43891B25</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslatePolyXZ</Name>
</Resource>
<Resource>
<ID>43E9B0A9</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScaleX</Name>
</Resource>
<Resource>
<ID>4443279C</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>CameraBlurKeyframe</Name>
</Resource>
<Resource>
<ID>4A2D76D1</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>DamageableTrigger</Name>
</Resource>
<Resource>
<ID>4C3D3E7F</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>GrapplePoint</Name>
</Resource>
<Resource>
<ID>4E133EF2</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>SpiderBallAttractionSurface</Name>
</Resource>
<Resource>
<ID>57F8715F</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Dock</Name>
</Resource>
<Resource>
<ID>592130DD</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslateZ</Name>
</Resource>
<Resource>
<ID>5BC862B8</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslateLinesXZ</Name>
</Resource>
<Resource>
<ID>5D08CCF6</ID>
<Type>TXTR</Type>
<Directory>script/mp3/</Directory>
<Name>WeaponGenerator</Name>
</Resource>
<Resource>
<ID>5F3B141B</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>VolumeBox</Name>
</Resource>
<Resource>
<ID>61B511E5</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>Checkerboard</Name>
</Resource>
<Resource>
<ID>6649251D</ID>
<Type>CMDL</Type>
<Directory>script/common/</Directory>
<Name>Camera</Name>
</Resource>
<Resource>
<ID>6A17B38F</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Sound</Name>
</Resource>
<Resource>
<ID>6B1FABDD</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>Cube</Name>
</Resource>
<Resource>
<ID>6B71C0F2</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScaleY</Name>
</Resource>
<Resource>
<ID>6C6CE7FE</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightSpotMask</Name>
</Resource>
<Resource>
<ID>6D5BC167</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Effect</Name>
</Resource>
<Resource>
<ID>6DD88D5D</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>RotationArrow</Name>
</Resource>
<Resource>
<ID>6E57F7E0</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>VolumeCylinder</Name>
</Resource>
<Resource>
<ID>72978DCF</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Relay</Name>
</Resource>
<Resource>
<ID>791A7BFD</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>SphereDoubleSided</Name>
</Resource>
<Resource>
<ID>7F494724</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>RotateX</Name>
</Resource>
<Resource>
<ID>81326A53</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScaleLinesYZ</Name>
</Resource>
<Resource>
<ID>825CDFA8</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScalePolyYZ</Name>
</Resource>
<Resource>
<ID>826C9800</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>ColorModulate</Name>
</Resource>
<Resource>
<ID>85CE16D7</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>StreamedMovie</Name>
</Resource>
<Resource>
<ID>875A6FD7</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>PointOfInterest</Name>
</Resource>
<Resource>
<ID>9039791A</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightDirectional</Name>
</Resource>
<Resource>
<ID>91437414</ID>
<Type>CMDL</Type>
<Directory>script/common/</Directory>
<Name>CameraWaypoint</Name>
</Resource>
<Resource>
<ID>92F9D13B</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScaleZ</Name>
</Resource>
<Resource>
<ID>95261BB8</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>WireSphere</Name>
</Resource>
<Resource>
<ID>968C405E</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>SoundModifier</Name>
</Resource>
<Resource>
<ID>9FF04AA1</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Generator</Name>
</Resource>
<Resource>
<ID>A6D1576D</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>RotateY</Name>
</Resource>
<Resource>
<ID>A82A3F02</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightSpot</Name>
</Resource>
<Resource>
<ID>AC18950A</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>RadialDamage</Name>
</Resource>
<Resource>
<ID>B0D52FF7</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightCustomMask</Name>
</Resource>
<Resource>
<ID>B0E09096</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightDirectionalMask</Name>
</Resource>
<Resource>
<ID>B23F1022</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>VisorGoo</Name>
</Resource>
<Resource>
<ID>B2DBCAED</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>PickupGenerator</Name>
</Resource>
<Resource>
<ID>B3050E38</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>SequenceTimer</Name>
</Resource>
<Resource>
<ID>B97DB26B</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Timer</Name>
</Resource>
<Resource>
<ID>BA7EA4F6</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Trigger</Name>
</Resource>
<Resource>
<ID>C0FBED3D</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScaleLinesXY</Name>
</Resource>
<Resource>
<ID>C2266292</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScalePolyXY</Name>
</Resource>
<Resource>
<ID>C4E83425</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>DistanceFog</Name>
</Resource>
<Resource>
<ID>C7BEFE29</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>VolumeSphere</Name>
</Resource>
<Resource>
<ID>CB904B3D</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>SpecialFunction</Name>
</Resource>
<Resource>
<ID>CE5967B6</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>RotateZ</Name>
</Resource>
<Resource>
<ID>D2F07DAF</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>ConditionalRelay</Name>
</Resource>
<Resource>
<ID>D7B6A50D</ID>
<Type>CMDL</Type>
<Directory></Directory>
<Name>Sphere</Name>
</Resource>
<Resource>
<ID>DA185196</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>VolumeCheckerboard</Name>
</Resource>
<Resource>
<ID>DC3787F2</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslatePolyYZ</Name>
</Resource>
<Resource>
<ID>E1A0D860</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>StreamedAudioModifier</Name>
</Resource>
<Resource>
<ID>E883FD86</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScaleLinesXZ</Name>
</Resource>
<Resource>
<ID>E9AE72DB</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>ScalePolyXZ</Name>
</Resource>
<Resource>
<ID>EE340FC4</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>MemoryRelay</Name>
</Resource>
<Resource>
<ID>F476CF85</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>TranslateLinesYZ</Name>
</Resource>
<Resource>
<ID>F76B369A</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>Counter</Name>
</Resource>
<Resource>
<ID>FB50DA78</ID>
<Type>CMDL</Type>
<Directory>editor/</Directory>
<Name>RotateClipOutline</Name>
</Resource>
<Resource>
<ID>FC8FA98B</ID>
<Type>TXTR</Type>
<Directory></Directory>
<Name>LightCustom</Name>
</Resource>
<Resource>
<ID>FD78FBC8</ID>
<Type>TXTR</Type>
<Directory>script/mp1/</Directory>
<Name>NewCameraShaker</Name>
</Resource>
<Resource>
<ID>FFAC2525</ID>
<Type>TXTR</Type>
<Directory>script/common/</Directory>
<Name>CameraShaker</Name>
</Resource>
</Resources>
</ResourceDB>

View File

@ -7,7 +7,7 @@
template<typename FlagEnum>
class TFlags
{
int mValue;
u32 mValue;
public:
TFlags() : mValue(0) {}

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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<SParameter> 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)

View File

@ -9,32 +9,39 @@ class CBinaryWriter : public IArchive
struct SParameter
{
u32 Offset;
u16 NumSubParams;
u32 NumSubParams;
bool Abstract;
};
std::vector<SParameter> 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)

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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)

View File

@ -10,24 +10,70 @@
#include <Common/Serialization/CXMLReader.h>
#include <Common/Serialization/CXMLWriter.h>
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);

View File

@ -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;

View File

@ -2,6 +2,7 @@
#include "CGameExporter.h"
#include "CGameProject.h"
#include "CResourceIterator.h"
#include "Core/IUIRelay.h"
#include "Core/Resource/CResource.h"
#include <Common/AssertMacro.h>
#include <Common/FileUtil.h>
@ -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<SDatabaseResource> 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;
}

View File

@ -24,12 +24,10 @@ class CResourceStore
CVirtualDirectory *mpDatabaseRoot;
std::map<CAssetID, CResourceEntry*> mResourceEntries;
std::map<CAssetID, CResourceEntry*> 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<typename ResType> ResType* LoadResource(const CAssetID& rkID) { return static_cast<ResType*>(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; }
};

View File

@ -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;

View File

@ -6,10 +6,18 @@
#include "WorldEditor/CWorldEditor.h"
#include "UICommon.h"
#include <QThread>
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)) );

View File

@ -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();