Added versioning support to the serializer; began restructuring CWorld and getting world cooking/serialization working for other games; gave CAssetID an alternate input stream constructor that takes an EGame instead of an EIDLength

This commit is contained in:
parax0 2016-08-14 04:56:51 -06:00
parent ba438383b4
commit 8f2b39469a
41 changed files with 304 additions and 200 deletions

View File

@ -53,6 +53,11 @@ CAssetID::CAssetID(IInputStream& rInput, EIDLength Length)
else mID = rInput.ReadLongLong();
}
CAssetID::CAssetID(IInputStream& rInput, EGame Game)
{
*this = CAssetID(rInput, (Game <= eEchoes ? e32Bit : e64Bit));
}
TString CAssetID::ToString() const
{
if (mLength == e32Bit)

View File

@ -1,6 +1,7 @@
#ifndef CASSETID_H
#define CASSETID_H
#include "EGame.h"
#include "TString.h"
#include "types.h"
#include <FileIO/FileIO.h>
@ -23,6 +24,7 @@ public:
CAssetID(u64 ID, EIDLength Length);
CAssetID(const char* pkID);
CAssetID(IInputStream& rInput, EIDLength Length);
CAssetID(IInputStream& rInput, EGame Game);
void Write(IOutputStream& rOutput) const;
TString ToString() const;
bool IsValid() const;
@ -49,6 +51,9 @@ public:
static CAssetID FromString(const TString& rkString);
static CAssetID RandomID();
inline static CAssetID InvalidID(EIDLength IDLength) { return (IDLength == e32Bit ? skInvalidID32 : skInvalidID64); }
inline static CAssetID InvalidID(EGame Game) { return InvalidID(Game <= eEchoes ? e32Bit : e64Bit); }
static CAssetID skInvalidID32;
static CAssetID skInvalidID64;
};

View File

@ -91,4 +91,5 @@ SOURCES += \
TString.cpp \
Log.cpp \
FileUtil.cpp \
CAssetID.cpp
CAssetID.cpp \
EGame.cpp

29
src/Common/EGame.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "EGame.h"
#include "CFourCC.h"
CFourCC GetGameID(EGame Game)
{
switch (Game)
{
case ePrimeDemo: return "MP1D";
case ePrime: return "MP1 ";
case eEchoesDemo: return "MP2D";
case eEchoes: return "MP2E";
case eCorruptionProto: return "MP3P";
case eCorruption: return "MP3C";
case eReturns: return "DKCR";
default: return "UNKN";
}
}
EGame GetGameForID(const CFourCC& rkID)
{
if (rkID == "MP1D") return ePrimeDemo;
if (rkID == "MP1 ") return ePrime;
if (rkID == "MP2D") return eEchoesDemo;
if (rkID == "MP2E") return eEchoes;
if (rkID == "MP3P") return eCorruptionProto;
if (rkID == "MP3C") return eCorruption;
if (rkID == "DKCR") return eReturns;
return eUnknownGame;
}

View File

@ -1,6 +1,11 @@
#ifndef EGAME_H
#define EGAME_H
#include "TString.h"
#include "types.h"
class CFourCC;
enum EGame
{
ePrimeDemo,
@ -13,4 +18,7 @@ enum EGame
eUnknownGame = -1
};
CFourCC GetGameID(EGame Game);
EGame GetGameForID(const CFourCC& rkID);
#endif // EGAME_H

View File

@ -15,9 +15,15 @@ public:
: IArchive()
, mJustEndedParam(false)
{
// Load XML and set current element to the root element
// Load XML and set current element to the root element; read version
mDoc.LoadFile(*rkFileName);
mpCurElem = mDoc.FirstChildElement();
ASSERT(mpCurElem != nullptr);
mFileVersion = TString( mpCurElem->Attribute("FileVer") ).ToInt32(10);
mArchiveVersion = TString( mpCurElem->Attribute("ArchiveVer") ).ToInt32(10);
const char *pkGameAttr = mpCurElem->Attribute("Game");
mGame = pkGameAttr ? eUnknownGame : GetGameForID( CFourCC(pkGameAttr) );
}
protected:

View File

@ -2,6 +2,7 @@
#define CXMLWRITER
#include "IArchive.h"
#include "Common/CFourCC.h"
#include <iostream>
#include <tinyxml2.h>
@ -12,16 +13,24 @@ class CXMLWriter : public IArchive
tinyxml2::XMLElement *mpCurElem;
public:
CXMLWriter(const TString& rkRootName, const TString& rkFileName)
CXMLWriter(const TString& rkFileName, const TString& rkRootName, u32 FileVersion, EGame Game = eUnknownGame)
: IArchive()
, mOutFilename(rkFileName)
{
mFileVersion = FileVersion;
mGame = Game;
// Create declaration and root node
tinyxml2::XMLDeclaration *pDecl = mDoc.NewDeclaration();
mDoc.LinkEndChild(pDecl);
mpCurElem = mDoc.NewElement(*rkRootName);
mDoc.LinkEndChild(mpCurElem);
// Write version data
mpCurElem->SetAttribute("FileVer", (int) FileVersion);
mpCurElem->SetAttribute("ArchiveVer", (int) skCurrentArchiveVersion);
if (Game != eUnknownGame) mpCurElem->SetAttribute("Game", *GetGameID(Game).ToString());
}
~CXMLWriter()

View File

@ -3,6 +3,7 @@
#include "Common/AssertMacro.h"
#include "Common/CAssetID.h"
#include "Common/EGame.h"
#include "Common/TString.h"
#include "Common/types.h"
@ -164,8 +165,15 @@ public:
// Actual archive class
class IArchive
{
protected:
s32 mFileVersion;
s32 mArchiveVersion;
EGame mGame;
public:
IArchive() {}
static const u32 skCurrentArchiveVersion = 0;
IArchive() : mFileVersion(0), mArchiveVersion(skCurrentArchiveVersion), mGame(eUnknownGame) {}
virtual ~IArchive() {}
#define ENABLE_FOR_SERIAL_TYPE(SType) typename std::enable_if<SerialType<ValType, IArchive>::Type == SerialType<ValType, IArchive>::##SType, int>::type = 0
@ -381,6 +389,11 @@ public:
virtual void SerializeHexPrimitive(u32& rValue) = 0;
virtual void SerializeHexPrimitive(s64& rValue) = 0;
virtual void SerializeHexPrimitive(u64& rValue) = 0;
// Accessors
inline u32 FileVersion() const { return mFileVersion; }
inline u32 ArchiveVersion() const { return mArchiveVersion; }
inline EGame Game() const { return mGame; }
};
// Container serialize methods

View File

@ -244,7 +244,7 @@ bool CResourceEntry::Save()
TString Dir = Path.GetFileDirectory();
FileUtil::CreateDirectory(Dir.ToUTF16());
CXMLWriter Writer(GetResourceSerialName(ResourceType()), Path);
CXMLWriter Writer(Path, GetResourceSerialName(ResourceType()), 0, mGame);
mpResource->Serialize(Writer);
}

View File

@ -234,6 +234,8 @@ public:
// For PlayerActor animsets we want to include only the empty suit (char 5) in the dependency list. This is to
// accomodate the dynamic loading the game does for PlayerActors to avoid having assets for suits the player
// doesn't have in memory. We want common assets (animations, etc) in the list but not per-character assets.
// The reason to include empty suit is to make sure resources that are stored as per-character data but are
// actually common to every character, such as particle effects, are still included in the list.
ASSERT(pEntry->ResourceType() == eAnimSet);
CAnimSetDependencyTree *pTree = static_cast<CAnimSetDependencyTree*>(pEntry->Dependencies());
mLayerUsedAssets.insert(pTree->ID());

View File

@ -24,7 +24,6 @@ class CGameArea : public CResource
friend class CAreaLoader;
friend class CAreaCooker;
EGame mVersion;
u32 mWorldIndex;
u32 mVertexCount;
u32 mTriangleCount;
@ -84,7 +83,6 @@ public:
void DeleteInstance(CScriptObject *pInstance);
// Inline Accessors
inline EGame Version() const { return mVersion; }
inline u32 WorldIndex() const { return mWorldIndex; }
inline CTransform4f Transform() const { return mTransform; }
inline u32 NumWorldModels() const { return mWorldModels.size(); }

View File

@ -4,7 +4,6 @@
CWorld::CWorld(CResourceEntry *pEntry /*= 0*/)
: CResource(pEntry)
, mWorldVersion(eUnknownGame)
, mpWorldName(nullptr)
, mpDarkWorldName(nullptr)
, mpSaveWorld(nullptr)
@ -56,24 +55,32 @@ void CWorld::SetAreaLayerInfo(CGameArea *pArea)
SArea::SLayer& rLayerInfo = AreaInfo.Layers[iLyr];
pLayer->SetName(rLayerInfo.LayerName);
pLayer->SetActive(rLayerInfo.EnabledByDefault);
pLayer->SetActive(rLayerInfo.Active);
}
}
// ************ SERIALIZATION ************
void CWorld::Serialize(IArchive& rArc)
{
rArc << SERIAL("WorldNameSTRG", mpWorldName)
<< SERIAL("DarkWorldNameSTRG", mpDarkWorldName)
<< SERIAL("WorldSaveInfo", mpSaveWorld)
<< SERIAL("DefaultSkyCMDL", mpDefaultSkybox)
<< SERIAL("MapWorld", mpMapWorld)
<< SERIAL("Unknown1", mUnknown1)
<< SERIAL("UnknownAreas", mUnknownAreas)
<< SERIAL("UnknownAGSC", mUnknownAGSC)
<< SERIAL_CONTAINER("MemoryRelays", mMemoryRelays, "MemoryRelay")
<< SERIAL_CONTAINER("Areas", mAreas, "Area")
<< SERIAL_CONTAINER("AudioGroups", mAudioGrps, "AudioGroup");
rArc << SERIAL("WorldNameSTRG", mpWorldName);
if (rArc.Game() == eEchoesDemo || rArc.Game() == eEchoes)
rArc << SERIAL("DarkWorldNameSTRG", mpDarkWorldName);
rArc << SERIAL("WorldSaveInfo", mpSaveWorld)
<< SERIAL("WorldMap", mpMapWorld)
<< SERIAL("DefaultSkyCMDL", mpDefaultSkybox);
if (rArc.Game() >= eEchoesDemo && rArc.Game() <= eCorruption)
rArc << SERIAL("TempleKeyWorldIndex", mTempleKeyWorldIndex);
if (rArc.Game() == ePrime)
rArc << SERIAL_CONTAINER("MemoryRelays", mMemoryRelays, "MemoryRelay");
rArc << SERIAL_CONTAINER("Areas", mAreas, "Area");
if (rArc.Game() <= ePrime)
rArc << SERIAL_CONTAINER("AudioGroups", mAudioGrps, "AudioGroup");
}
void Serialize(IArchive& rArc, CWorld::SMemoryRelay& rMemRelay)
@ -81,7 +88,7 @@ void Serialize(IArchive& rArc, CWorld::SMemoryRelay& rMemRelay)
rArc << SERIAL_HEX("MemoryRelayID", rMemRelay.InstanceID)
<< SERIAL_HEX("TargetID", rMemRelay.TargetID)
<< SERIAL("Message", rMemRelay.Message)
<< SERIAL("Unknown", rMemRelay.Unknown);
<< SERIAL("Active", rMemRelay.Active);
}
void Serialize(IArchive& rArc, CWorld::SArea& rArea)
@ -91,13 +98,11 @@ void Serialize(IArchive& rArc, CWorld::SArea& rArea)
<< SERIAL("Transform", rArea.Transform)
<< SERIAL("BoundingBox", rArea.AetherBox)
<< SERIAL("AreaMREA", rArea.AreaResID)
<< SERIAL_HEX("AreaID", rArea.AreaID)
<< SERIAL("AreaID", rArea.AreaID)
<< SERIAL("AllowPakDuplicates", rArea.AllowPakDuplicates)
<< SERIAL_CONTAINER("AttachedAreas", rArea.AttachedAreaIDs, "AreaIndex")
<< SERIAL_CONTAINER("Dependencies", rArea.Dependencies, "Dependency")
<< SERIAL_CONTAINER("RelModules", rArea.RelFilenames, "Module")
<< SERIAL_CONTAINER("RelOffsets", rArea.RelOffsets, "Offset")
<< SERIAL("CommonDependsStart", rArea.CommonDependenciesStart)
<< SERIAL_CONTAINER("Docks", rArea.Docks, "Dock")
<< SERIAL_CONTAINER("Layers", rArea.Layers, "Layer");
}
@ -117,12 +122,11 @@ void Serialize(IArchive& rArc, CWorld::SArea::SDock::SConnectingDock& rDock)
void Serialize(IArchive& rArc, CWorld::SArea::SLayer& rLayer)
{
rArc << SERIAL("Name", rLayer.LayerName)
<< SERIAL("DefaultEnabled", rLayer.EnabledByDefault)
<< SERIAL("LayerDependsStart", rLayer.LayerDependenciesStart);
<< SERIAL("Active", rLayer.Active);
}
void Serialize(IArchive& rArc, CWorld::SAudioGrp& rAudioGrp)
{
rArc << SERIAL("StudioID", rAudioGrp.Unknown)
rArc << SERIAL("GroupID", rAudioGrp.GroupID)
<< SERIAL("AGSC", rAudioGrp.ResID);
}

View File

@ -14,21 +14,17 @@ class CWorld : public CResource
friend class CWorldCooker;
// Instances of CResource pointers are placeholders for unimplemented resource types (eg CMapWorld)
EGame mWorldVersion;
TResPtr<CStringTable> mpWorldName;
TResPtr<CStringTable> mpDarkWorldName;
TResPtr<CResource> mpSaveWorld;
TResPtr<CModel> mpDefaultSkybox;
TResPtr<CResource> mpMapWorld;
u32 mTempleKeyWorldIndex;
u32 mUnknown1;
u32 mUnknownAreas;
u32 mUnknownAGSC;
struct SAudioGrp
{
CAssetID ResID;
u32 Unknown;
u32 GroupID;
};
std::vector<SAudioGrp> mAudioGrps;
@ -37,7 +33,7 @@ class CWorld : public CResource
u32 InstanceID;
u32 TargetID;
u16 Message;
u8 Unknown;
bool Active;
};
std::vector<SMemoryRelay> mMemoryRelays;
@ -47,15 +43,14 @@ class CWorld : public CResource
TResPtr<CStringTable> pAreaName;
CTransform4f Transform;
CAABox AetherBox;
CAssetID AreaResID; // Loading every single area as a CResource would be a very bad idea
u64 AreaID;
CAssetID AreaResID; // Area resource ID
CAssetID AreaID; // Internal area ID (same length as an asset ID)
bool AllowPakDuplicates;
std::vector<SMemoryRelay> MemoryRelays; // Only needed for MP1
std::vector<u16> AttachedAreaIDs;
std::vector<CAssetID> Dependencies;
std::vector<TString> RelFilenames;
std::vector<TString> RelFilenames; // Needs to be removed & generated at cook; temporarily leaving for debugging
std::vector<u32> RelOffsets;
u32 CommonDependenciesStart;
struct SDock
{
@ -72,9 +67,8 @@ class CWorld : public CResource
struct SLayer
{
TString LayerName;
bool EnabledByDefault;
bool Active;
u8 LayerID[16];
u32 LayerDependenciesStart; // Offset into Dependencies vector
};
std::vector<SLayer> Layers;
};
@ -97,7 +91,6 @@ public:
friend void Serialize(IArchive& rArc, SAudioGrp& rAudioGrp);
// Accessors
inline EGame Version() const { return mWorldVersion; }
inline CStringTable* WorldName() const { return mpWorldName; }
inline CStringTable* DarkWorldName() const { return mpDarkWorldName; }
inline CResource* SaveWorld() const { return mpSaveWorld; }

View File

@ -310,7 +310,7 @@ void CAreaCooker::WriteCookedArea(CGameArea *pArea, IOutputStream& rOut)
{
CAreaCooker Cooker;
Cooker.mpArea = pArea;
Cooker.mVersion = pArea->Version();
Cooker.mVersion = pArea->Game();
if (Cooker.mVersion <= eEchoes)
Cooker.DetermineSectionNumbersPrime();

View File

@ -9,34 +9,46 @@ CWorldCooker::CWorldCooker()
bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
{
ASSERT(rMLVL.IsValid());
EGame Game = pWorld->Game();
// MLVL Header
rMLVL.WriteLong(0xDEAFBABE);
rMLVL.WriteLong( GetMLVLVersion(pWorld->Game()) );
CAssetID WorldNameID = pWorld->mpWorldName ? pWorld->mpWorldName->ID() : CAssetID::skInvalidID32;
CAssetID SaveWorldID = pWorld->mpSaveWorld ? pWorld->mpSaveWorld->ID() : CAssetID::skInvalidID32;
CAssetID DefaultSkyID = pWorld->mpDefaultSkybox ? pWorld->mpDefaultSkybox->ID() : CAssetID::skInvalidID32;
CAssetID WorldNameID = pWorld->mpWorldName ? pWorld->mpWorldName->ID() : CAssetID::InvalidID(Game);
CAssetID DarkWorldNameID = pWorld->mpDarkWorldName ? pWorld->mpDarkWorldName->ID() : CAssetID::InvalidID(Game);
CAssetID SaveWorldID = pWorld->mpSaveWorld ? pWorld->mpSaveWorld->ID() : CAssetID::InvalidID(Game);
CAssetID DefaultSkyID = pWorld->mpDefaultSkybox ? pWorld->mpDefaultSkybox->ID() : CAssetID::InvalidID(Game);
WorldNameID.Write(rMLVL);
if (Game == eEchoesDemo || Game == eEchoes)
{
DarkWorldNameID.Write(rMLVL);
rMLVL.WriteLong(0);
}
SaveWorldID.Write(rMLVL);
DefaultSkyID.Write(rMLVL);
// Memory Relays
rMLVL.WriteLong( pWorld->mMemoryRelays.size() );
for (u32 iMem = 0; iMem < pWorld->mMemoryRelays.size(); iMem++)
if (Game == ePrime)
{
CWorld::SMemoryRelay& rRelay = pWorld->mMemoryRelays[iMem];
rMLVL.WriteLong(rRelay.InstanceID);
rMLVL.WriteLong(rRelay.TargetID);
rMLVL.WriteShort(rRelay.Message);
rMLVL.WriteByte(rRelay.Unknown);
rMLVL.WriteLong( pWorld->mMemoryRelays.size() );
for (u32 iMem = 0; iMem < pWorld->mMemoryRelays.size(); iMem++)
{
CWorld::SMemoryRelay& rRelay = pWorld->mMemoryRelays[iMem];
rMLVL.WriteLong(rRelay.InstanceID);
rMLVL.WriteLong(rRelay.TargetID);
rMLVL.WriteShort(rRelay.Message);
rMLVL.WriteBool(rRelay.Active);
}
}
// Areas
rMLVL.WriteLong(pWorld->mAreas.size());
rMLVL.WriteLong(1); // Unknown
if (Game <= ePrime) rMLVL.WriteLong(1); // Unknown
for (u32 iArea = 0; iArea < pWorld->mAreas.size(); iArea++)
{
@ -45,85 +57,123 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
CResourceEntry *pAreaEntry = gpResourceStore->FindEntry(rArea.AreaResID);
ASSERT(pAreaEntry && pAreaEntry->ResourceType() == eArea);
CAssetID AreaNameID = rArea.pAreaName ? rArea.pAreaName->ID() : CAssetID::skInvalidID32;
CAssetID AreaNameID = rArea.pAreaName ? rArea.pAreaName->ID() : CAssetID::InvalidID(Game);
AreaNameID.Write(rMLVL);
rArea.Transform.Write(rMLVL);
rArea.AetherBox.Write(rMLVL);
rArea.AreaResID.Write(rMLVL);
rMLVL.WriteLong( (u32) rArea.AreaID );
rArea.AreaID.Write(rMLVL);
// Attached Areas
rMLVL.WriteLong( rArea.AttachedAreaIDs.size() );
if (Game <= eCorruption)
{
rMLVL.WriteLong( rArea.AttachedAreaIDs.size() );
for (u32 iAttach = 0; iAttach < rArea.AttachedAreaIDs.size(); iAttach++)
rMLVL.WriteShort(rArea.AttachedAreaIDs[iAttach]);
for (u32 iAttach = 0; iAttach < rArea.AttachedAreaIDs.size(); iAttach++)
rMLVL.WriteShort(rArea.AttachedAreaIDs[iAttach]);
}
// Dependencies
std::list<CAssetID> Dependencies;
std::list<u32> LayerDependsOffsets;
CAreaDependencyListBuilder Builder(pAreaEntry);
Builder.BuildDependencyList(Dependencies, LayerDependsOffsets);
rMLVL.WriteLong(0);
rMLVL.WriteLong( Dependencies.size() );
for (auto Iter = Dependencies.begin(); Iter != Dependencies.end(); Iter++)
if (Game <= eEchoes)
{
CAssetID ID = *Iter;
CResourceEntry *pEntry = gpResourceStore->FindEntry(ID);
ID.Write(rMLVL);
pEntry->CookedExtension().Write(rMLVL);
}
std::list<CAssetID> Dependencies;
std::list<u32> LayerDependsOffsets;
CAreaDependencyListBuilder Builder(pAreaEntry);
Builder.BuildDependencyList(Dependencies, LayerDependsOffsets);
rMLVL.WriteLong(LayerDependsOffsets.size());
rMLVL.WriteLong(0);
rMLVL.WriteLong( Dependencies.size() );
for (auto Iter = LayerDependsOffsets.begin(); Iter != LayerDependsOffsets.end(); Iter++)
rMLVL.WriteLong(*Iter);
// Docks
rMLVL.WriteLong( rArea.Docks.size() );
for (u32 iDock = 0; iDock < rArea.Docks.size(); iDock++)
{
CWorld::SArea::SDock& rDock = rArea.Docks[iDock];
rMLVL.WriteLong( rDock.ConnectingDocks.size() );
for (u32 iCon = 0; iCon < rDock.ConnectingDocks.size(); iCon++)
for (auto Iter = Dependencies.begin(); Iter != Dependencies.end(); Iter++)
{
CWorld::SArea::SDock::SConnectingDock& rConDock = rDock.ConnectingDocks[iCon];
rMLVL.WriteLong(rConDock.AreaIndex);
rMLVL.WriteLong(rConDock.DockIndex);
CAssetID ID = *Iter;
CResourceEntry *pEntry = gpResourceStore->FindEntry(ID);
ID.Write(rMLVL);
pEntry->CookedExtension().Write(rMLVL);
}
rMLVL.WriteLong( rDock.DockCoordinates.size() );
rMLVL.WriteLong(LayerDependsOffsets.size());
for (u32 iCoord = 0; iCoord < rDock.DockCoordinates.size(); iCoord++)
rDock.DockCoordinates[iCoord].Write(rMLVL);
for (auto Iter = LayerDependsOffsets.begin(); Iter != LayerDependsOffsets.end(); Iter++)
rMLVL.WriteLong(*Iter);
}
// Docks
if (Game <= eCorruption)
{
rMLVL.WriteLong( rArea.Docks.size() );
for (u32 iDock = 0; iDock < rArea.Docks.size(); iDock++)
{
CWorld::SArea::SDock& rDock = rArea.Docks[iDock];
rMLVL.WriteLong( rDock.ConnectingDocks.size() );
for (u32 iCon = 0; iCon < rDock.ConnectingDocks.size(); iCon++)
{
CWorld::SArea::SDock::SConnectingDock& rConDock = rDock.ConnectingDocks[iCon];
rMLVL.WriteLong(rConDock.AreaIndex);
rMLVL.WriteLong(rConDock.DockIndex);
}
rMLVL.WriteLong( rDock.DockCoordinates.size() );
for (u32 iCoord = 0; iCoord < rDock.DockCoordinates.size(); iCoord++)
rDock.DockCoordinates[iCoord].Write(rMLVL);
}
}
// Module Dependencies
if (Game == eEchoesDemo || Game == eEchoes)
{
std::vector<TString> ModuleNames;
std::vector<u32> ModuleLayerOffsets;
CAreaDependencyTree *pAreaDeps = static_cast<CAreaDependencyTree*>(pAreaEntry->Dependencies());
pAreaDeps->GetModuleDependencies(Game, ModuleNames, ModuleLayerOffsets);
rMLVL.WriteLong(ModuleNames.size());
for (u32 iMod = 0; iMod < ModuleNames.size(); iMod++)
rMLVL.WriteString(ModuleNames[iMod].ToStdString());
rMLVL.WriteLong(ModuleLayerOffsets.size());
for (u32 iOff = 0; iOff < ModuleLayerOffsets.size(); iOff++)
rMLVL.WriteLong(ModuleLayerOffsets[iOff]);
}
}
CAssetID MapWorldID = pWorld->mpMapWorld ? pWorld->mpMapWorld->ID() : CAssetID::skInvalidID32;
MapWorldID.Write(rMLVL);
rMLVL.WriteByte(0);
rMLVL.WriteLong(0);
// Audio Groups
rMLVL.WriteLong(pWorld->mAudioGrps.size());
for (u32 iGrp = 0; iGrp < pWorld->mAudioGrps.size(); iGrp++)
if (Game <= eCorruption)
{
CWorld::SAudioGrp& rAudioGroup = pWorld->mAudioGrps[iGrp];
rMLVL.WriteLong(rAudioGroup.Unknown);
rAudioGroup.ResID.Write(rMLVL);
// World Map
CAssetID MapWorldID = pWorld->mpMapWorld ? pWorld->mpMapWorld->ID() : CAssetID::skInvalidID32;
MapWorldID.Write(rMLVL);
// Script Layer - unused in all retail builds but this will need to be supported eventually to properly support the MP1 demo
rMLVL.WriteByte(0);
rMLVL.WriteLong(0);
}
rMLVL.WriteByte(0);
// Audio Groups
if (Game <= ePrime)
{
rMLVL.WriteLong(pWorld->mAudioGrps.size());
for (u32 iGrp = 0; iGrp < pWorld->mAudioGrps.size(); iGrp++)
{
CWorld::SAudioGrp& rAudioGroup = pWorld->mAudioGrps[iGrp];
rMLVL.WriteLong(rAudioGroup.GroupID);
rAudioGroup.ResID.Write(rMLVL);
}
rMLVL.WriteByte(0);
}
// Layers
rMLVL.WriteLong(pWorld->mAreas.size());
std::vector<TString> LayerNames;
std::vector<u32> LayerNameOffsets;
// Layer Flags
for (u32 iArea = 0; iArea < pWorld->mAreas.size(); iArea++)
{
CWorld::SArea& rArea = pWorld->mAreas[iArea];
@ -135,7 +185,7 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
for (u32 iLyr = 0; iLyr < rArea.Layers.size(); iLyr++)
{
CWorld::SArea::SLayer& rLayer = rArea.Layers[iLyr];
if (!rLayer.EnabledByDefault)
if (!rLayer.Active)
LayerActiveFlags &= ~(1 << iLyr);
LayerNames.push_back(rLayer.LayerName);
@ -144,11 +194,18 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
rMLVL.WriteLongLong(LayerActiveFlags);
}
// Layer Names
rMLVL.WriteLong(LayerNames.size());
for (u32 iLyr = 0; iLyr < LayerNames.size(); iLyr++)
rMLVL.WriteString(LayerNames[iLyr].ToStdString());
// todo: Layer Saved State IDs go here for MP3/DKCR; need support for saved state IDs to implement
if (Game == eCorruption || Game == eReturns)
{
}
// Layer Name Offsets
rMLVL.WriteLong(LayerNameOffsets.size());
for (u32 iOff = 0; iOff < LayerNameOffsets.size(); iOff++)

View File

@ -590,19 +590,19 @@ void CAreaLoader::ReadCollision()
void CAreaLoader::ReadPATH()
{
mpSectionMgr->ToSection(mPathBlockNum);
mpArea->mPathID = CAssetID(*mpMREA, (mVersion <= eEchoes ? e32Bit : e64Bit));
mpArea->mPathID = CAssetID(*mpMREA, mVersion);
}
void CAreaLoader::ReadPTLA()
{
mpSectionMgr->ToSection(this->mPTLABlockNum);
mpArea->mPortalAreaID = CAssetID(*mpMREA, (mVersion <= eEchoes ? e32Bit : e64Bit));
mpArea->mPortalAreaID = CAssetID(*mpMREA, mVersion);
}
void CAreaLoader::ReadEGMC()
{
mpSectionMgr->ToSection(mEGMCBlockNum);
CAssetID EGMC(*mpMREA, (mVersion <= eEchoes ? e32Bit : e64Bit));
CAssetID EGMC(*mpMREA, mVersion);
mpArea->mpPoiToWorldMap = gpResourceStore->LoadResource(EGMC, "EGMC");
}
@ -669,7 +669,6 @@ CGameArea* CAreaLoader::LoadMREA(IInputStream& MREA, CResourceEntry *pEntry)
u32 Version = MREA.ReadLong();
Loader.mVersion = GetFormatVersion(Version);
Loader.mpArea->SetGame(Loader.mVersion);
Loader.mpArea->mVersion = Loader.mVersion;
Loader.mpMREA = &MREA;
switch (Loader.mVersion)

View File

@ -34,7 +34,6 @@ CDependencyGroup* CDependencyGroupLoader::LoadDGRP(IInputStream& rDGRP, CResourc
u32 NumDependencies = rDGRP.ReadLong();
EGame Game = VersionTest(rDGRP, NumDependencies);
EIDLength IDLength = (Game < eCorruptionProto ? e32Bit : e64Bit);
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
pGroup->SetGame(Game);
@ -42,7 +41,7 @@ CDependencyGroup* CDependencyGroupLoader::LoadDGRP(IInputStream& rDGRP, CResourc
for (u32 iDep = 0; iDep < NumDependencies; iDep++)
{
rDGRP.Seek(0x4, SEEK_CUR); // Skip dependency type
CAssetID AssetID(rDGRP, IDLength);
CAssetID AssetID(rDGRP, Game);
pGroup->AddDependency(AssetID);
}

View File

@ -110,7 +110,7 @@ void CScriptLoader::ReadProperty(IProperty *pProp, u32 Size, IInputStream& rSCLY
{
TFileProperty *pFileCast = static_cast<TFileProperty*>(pProp);
CAssetID ResID = (mVersion < eCorruptionProto ? rSCLY.ReadLong() : rSCLY.ReadLongLong());
CAssetID ResID(rSCLY, mVersion);
const TStringList& rkExtensions = static_cast<CFileTemplate*>(pTemp)->Extensions();
CResourceInfo Info(ResID, CFourCC(!rkExtensions.empty() ? rkExtensions.front() : "UNKN"));

View File

@ -190,8 +190,6 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadHINT(IInputStream& rHINT, CResou
return nullptr;
}
EIDLength IDLength = (Game <= eEchoes ? e32Bit : e64Bit);
// Read main file
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
u32 NumHints = rHINT.ReadLong();
@ -200,15 +198,15 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadHINT(IInputStream& rHINT, CResou
{
rHINT.ReadString(); // Skip hint name
rHINT.Seek(0x8, SEEK_CUR); // Skip unknown + appear time
pGroup->AddDependency( CAssetID(rHINT, IDLength) ); // Pop-up STRG
pGroup->AddDependency( CAssetID(rHINT, Game) ); // Pop-up STRG
rHINT.Seek(0x8, SEEK_CUR); // Skip unknowns
if (Game <= eEchoes)
{
pGroup->AddDependency( CAssetID(rHINT, IDLength) ); // Target MLVL
pGroup->AddDependency( CAssetID(rHINT, IDLength) ); // Target MREA
pGroup->AddDependency( CAssetID(rHINT, Game) ); // Target MLVL
pGroup->AddDependency( CAssetID(rHINT, Game) ); // Target MREA
rHINT.Seek(0x4, SEEK_CUR); // Skip target room index
pGroup->AddDependency( CAssetID(rHINT, IDLength) ); // Map STRG
pGroup->AddDependency( CAssetID(rHINT, Game) ); // Map STRG
}
}

View File

@ -997,7 +997,7 @@ void CUnsupportedParticleLoader::ParseAssetFunction(IInputStream& rFile)
break;
case kAssetCNST:
mpGroup->AddDependency( CAssetID(rFile, mpGroup->Game() <= eEchoes ? e32Bit : e64Bit) );
mpGroup->AddDependency( CAssetID(rFile, mpGroup->Game()) );
break;
default:
@ -1015,7 +1015,6 @@ void CUnsupportedParticleLoader::ParseSpawnSystemKeyframeData(IInputStream& rFil
rFile.Seek(0x10, SEEK_CUR); // Skip unneeded values
u32 Count = rFile.ReadLong();
EIDLength IDLength = (mpGroup->Game() <= eEchoes ? e32Bit : e64Bit);
for (u32 iKey = 0; iKey < Count; iKey++)
{
@ -1024,7 +1023,7 @@ void CUnsupportedParticleLoader::ParseSpawnSystemKeyframeData(IInputStream& rFil
for (u32 iInfo = 0; iInfo < InfoCount; iInfo++)
{
mpGroup->AddDependency( CAssetID(rFile, IDLength) );
mpGroup->AddDependency( CAssetID(rFile, mpGroup->Game()) );
rFile.Seek(0xC, SEEK_CUR); // Skip unknown/unneeded values
}
}

View File

@ -17,7 +17,7 @@ void CWorldLoader::LoadPrimeMLVL(IInputStream& rMLVL)
{
mpWorld->mpWorldName = gpResourceStore->LoadResource(rMLVL.ReadLong(), "STRG");
if (mVersion == eEchoes) mpWorld->mpDarkWorldName = gpResourceStore->LoadResource(rMLVL.ReadLong(), "STRG");
if (mVersion >= eEchoes) mpWorld->mUnknown1 = rMLVL.ReadLong();
if (mVersion >= eEchoes) mpWorld->mTempleKeyWorldIndex = rMLVL.ReadLong();
if (mVersion >= ePrime) mpWorld->mpSaveWorld = gpResourceStore->LoadResource(rMLVL.ReadLong(), "SAVW");
mpWorld->mpDefaultSkybox = gpResourceStore->LoadResource(rMLVL.ReadLong(), "CMDL");
}
@ -42,40 +42,25 @@ void CWorldLoader::LoadPrimeMLVL(IInputStream& rMLVL)
MemRelay.InstanceID = rMLVL.ReadLong();
MemRelay.TargetID = rMLVL.ReadLong();
MemRelay.Message = rMLVL.ReadShort();
MemRelay.Unknown = rMLVL.ReadByte();
MemRelay.Active = rMLVL.ReadBool();
mpWorld->mMemoryRelays.push_back(MemRelay);
}
}
// Areas - here's the real meat of the file
u32 NumAreas = rMLVL.ReadLong();
if (mVersion == ePrime) mpWorld->mUnknownAreas = rMLVL.ReadLong();
if (mVersion == ePrime) rMLVL.Seek(0x4, SEEK_CUR);
mpWorld->mAreas.resize(NumAreas);
for (u32 iArea = 0; iArea < NumAreas; iArea++)
{
// Area header
CWorld::SArea *pArea = &mpWorld->mAreas[iArea];
if (mVersion < eCorruptionProto)
pArea->pAreaName = gpResourceStore->LoadResource(rMLVL.ReadLong(), "STRG");
else
pArea->pAreaName = gpResourceStore->LoadResource(rMLVL.ReadLongLong(), "STRG");
pArea->pAreaName = gpResourceStore->LoadResource( CAssetID(rMLVL, mVersion), "STRG" );
pArea->Transform = CTransform4f(rMLVL);
pArea->AetherBox = CAABox(rMLVL);
if (mVersion < eCorruptionProto)
{
pArea->AreaResID = rMLVL.ReadLong() & 0xFFFFFFFF;
pArea->AreaID = rMLVL.ReadLong() & 0xFFFFFFFF;
}
else
{
pArea->AreaResID = rMLVL.ReadLongLong();
pArea->AreaID = rMLVL.ReadLongLong();
}
pArea->AreaResID = CAssetID(rMLVL, mVersion);
pArea->AreaID = CAssetID(rMLVL, mVersion);
// Attached areas
u32 NumAttachedAreas = rMLVL.ReadLong();
@ -83,36 +68,15 @@ void CWorldLoader::LoadPrimeMLVL(IInputStream& rMLVL)
for (u32 iAttached = 0; iAttached < NumAttachedAreas; iAttached++)
pArea->AttachedAreaIDs.push_back( rMLVL.ReadShort() );
if (mVersion < eCorruptionProto)
rMLVL.Seek(0x4, SEEK_CUR); // Skipping unknown value (always 0)
// Dependencies
// Skip dependency list - this is very fast to regenerate so there's no use in caching it
if (mVersion < eCorruptionProto)
{
rMLVL.Seek(0x4, SEEK_CUR);
u32 NumDependencies = rMLVL.ReadLong();
pArea->Dependencies.reserve(NumDependencies);
rMLVL.Seek(NumDependencies * 8, SEEK_CUR);
for (u32 iDep = 0; iDep < NumDependencies; iDep++)
{
pArea->Dependencies.push_back( CAssetID(rMLVL, e32Bit) );
rMLVL.Seek(0x4, SEEK_CUR);
}
/**
* Dependency offsets - indicates an offset into the dependency list where each layer's dependencies start
* The count is the layer count + 1 because the last offset is for common dependencies, like terrain textures
*/
u32 NumDependencyOffsets = rMLVL.ReadLong();
pArea->Layers.resize(NumDependencyOffsets - 1);
for (u32 iOff = 0; iOff < NumDependencyOffsets; iOff++)
{
u32 *pTarget;
if (iOff == NumDependencyOffsets - 1) pTarget = &pArea->CommonDependenciesStart;
else pTarget = &pArea->Layers[iOff].LayerDependenciesStart;
*pTarget = rMLVL.ReadLong();
}
rMLVL.Seek(NumDependencyOffsets * 4, SEEK_CUR);
}
// Docks
@ -161,16 +125,15 @@ void CWorldLoader::LoadPrimeMLVL(IInputStream& rMLVL)
}
}
// Footer
// Internal name - MP1 doesn't have this so reuse the area's real name
if (mVersion >= eEchoesDemo)
pArea->InternalName = rMLVL.ReadString();
else
pArea->InternalName = (pArea->pAreaName ? pArea->pAreaName->String("ENGL", 0).ToUTF8() : "");
}
// MapWorld
if (mVersion < eCorruptionProto)
mpWorld->mpMapWorld = gpResourceStore->LoadResource(rMLVL.ReadLong(), "MAPW");
else
mpWorld->mpMapWorld = gpResourceStore->LoadResource(rMLVL.ReadLongLong(), "MAPW");
mpWorld->mpMapWorld = gpResourceStore->LoadResource( CAssetID(rMLVL, mVersion), "MAPW" );
rMLVL.Seek(0x5, SEEK_CUR); // Unknown values which are always 0
// AudioGrps
@ -182,7 +145,7 @@ void CWorldLoader::LoadPrimeMLVL(IInputStream& rMLVL)
for (u32 iGrp = 0; iGrp < NumAudioGrps; iGrp++)
{
CWorld::SAudioGrp AudioGrp;
AudioGrp.Unknown = rMLVL.ReadLong();
AudioGrp.GroupID = rMLVL.ReadLong();
AudioGrp.ResID = rMLVL.ReadLong() & 0xFFFFFFFF;
mpWorld->mAudioGrps.push_back(AudioGrp);
}
@ -200,7 +163,7 @@ void CWorldLoader::LoadPrimeMLVL(IInputStream& rMLVL)
u64 LayerFlags = rMLVL.ReadLongLong();
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
pArea->Layers[iLayer].EnabledByDefault = (((LayerFlags >> iLayer) & 0x1) == 1);
pArea->Layers[iLayer].Active = (((LayerFlags >> iLayer) & 0x1) == 1);
}
// Layer names
@ -262,7 +225,7 @@ void CWorldLoader::LoadReturnsMLVL(IInputStream& rMLVL)
u64 LayerFlags = rMLVL.ReadLongLong();
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
pArea->Layers[iLayer].EnabledByDefault = (((LayerFlags >> iLayer) & 0x1) == 1);
pArea->Layers[iLayer].Active = (((LayerFlags >> iLayer) & 0x1) == 1);
}
// Layer names
@ -303,7 +266,6 @@ CWorld* CWorldLoader::LoadMLVL(IInputStream& rMLVL, CResourceEntry *pEntry)
CWorldLoader Loader;
Loader.mpWorld = new CWorld(pEntry);
Loader.mpWorld->SetGame(Version);
Loader.mpWorld->mWorldVersion = Version;
Loader.mVersion = Version;
if (Version != eReturns)

View File

@ -95,7 +95,7 @@ void CPropertyView::SetInstance(CScriptObject *pObj)
void CPropertyView::UpdateEditorProperties(const QModelIndex& rkParent)
{
// Check what game this is
EGame Game = mpEditor->ActiveArea()->Version();
EGame Game = mpEditor->CurrentGame();
// Iterate over all properties and update if they're an editor property.
for (int iRow = 0; iRow < mpModel->rowCount(rkParent); iRow++)

View File

@ -95,7 +95,7 @@ void CDeleteSelectionCommand::undo()
mpEditor->NotifyNodeAboutToBeSpawned();
CMemoryInStream Mem(rNode.InstanceData.data(), rNode.InstanceData.size(), IOUtil::eBigEndian);
CScriptObject *pInstance = CScriptLoader::LoadInstance(Mem, rNode.pArea, rNode.pLayer, rNode.pArea->Version(), true);
CScriptObject *pInstance = CScriptLoader::LoadInstance(Mem, rNode.pArea, rNode.pLayer, rNode.pArea->Game(), true);
CScriptNode *pNode = mpEditor->Scene()->CreateScriptNode(pInstance, rNode.NodeID);
rNode.pArea->AddInstanceToArea(pInstance);
rNode.pLayer->AddInstance(pInstance, rNode.LayerIndex);

View File

@ -55,7 +55,7 @@ void CPasteNodesCommand::redo()
if (rkNode.Type == eScriptNode)
{
CMemoryInStream In(rkNode.InstanceData.data(), rkNode.InstanceData.size(), IOUtil::eBigEndian);
CScriptObject *pInstance = CScriptLoader::LoadInstance(In, pArea, mpLayer, pArea->Version(), true);
CScriptObject *pInstance = CScriptLoader::LoadInstance(In, pArea, mpLayer, pArea->Game(), true);
pArea->AddInstanceToArea(pInstance);
mpLayer->AddInstance(pInstance);

View File

@ -343,7 +343,7 @@ void CPoiMapEditDialog::StopPicking()
void CPoiMapEditDialog::OnInstanceListButtonClicked()
{
EGame Game = mpEditor->ActiveArea()->Version();
EGame Game = mpEditor->CurrentGame();
CScriptTemplate *pPoiTemplate = CMasterTemplate::MasterForGame(Game)->TemplateByID("POIN");
CPoiListDialog Dialog(pPoiTemplate, &mSourceModel, mpEditor->Scene(), this);

View File

@ -212,16 +212,16 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea)
UpdateCameraOrbit();
// Default bloom to Fake Bloom for Metroid Prime 3; disable for other games
bool AllowBloom = (mpWorld->Version() == eCorruptionProto || mpWorld->Version() == eCorruption);
bool AllowBloom = (mpWorld->Game() == eCorruptionProto || mpWorld->Game() == eCorruption);
AllowBloom ? SetFakeBloom() : SetNoBloom();
ui->menuBloom->setEnabled(AllowBloom);
// Disable EGMC editing for Prime 1 and DKCR
bool AllowEGMC = ( (mpWorld->Version() >= eEchoesDemo) && (mpWorld->Version() <= eCorruption) );
bool AllowEGMC = ( (mpWorld->Game() >= eEchoesDemo) && (mpWorld->Game() <= eCorruption) );
ui->ActionEditPoiToWorldMap->setEnabled(AllowEGMC);
// Set up sidebar tabs
CMasterTemplate *pMaster = CMasterTemplate::MasterForGame(mpArea->Version());
CMasterTemplate *pMaster = CMasterTemplate::MasterForGame(mpArea->Game());
ui->CreateTabContents->SetMaster(pMaster);
ui->InstancesTabContents->SetMaster(pMaster);

View File

@ -59,7 +59,7 @@ public:
bool HasAnyScriptNodesSelected() const;
inline CGameArea* ActiveArea() const { return mpArea; }
inline EGame CurrentGame() const { return mpArea ? mpArea->Version() : eUnknownGame; }
inline EGame CurrentGame() const { return mpArea ? mpArea->Game() : eUnknownGame; }
inline CLinkDialog* LinkDialog() const { return mpLinkDialog; }
CSceneViewport* Viewport() const;

View File

@ -313,7 +313,7 @@ void WInstancesTab::OnHideAllExceptTypeAction()
else
{
EGame Game = mpEditor->ActiveArea()->Version();
EGame Game = mpEditor->CurrentGame();
CMasterTemplate *pMaster = CMasterTemplate::MasterForGame(Game);
for (u32 iTemp = 0; iTemp < pMaster->NumScriptTemplates(); iTemp++)
@ -344,7 +344,7 @@ void WInstancesTab::OnUnhideAllTypes()
else
{
EGame Game = mpEditor->ActiveArea()->Version();
EGame Game = mpEditor->CurrentGame();
CMasterTemplate *pMaster = CMasterTemplate::MasterForGame(Game);
for (u32 iTemp = 0; iTemp < pMaster->NumScriptTemplates(); iTemp++)
@ -378,7 +378,7 @@ void WInstancesTab::OnUnhideAll()
if (TypesRoot.isValid())
{
EGame Game = mpEditor->ActiveArea()->Version();
EGame Game = mpEditor->CurrentGame();
CMasterTemplate *pMaster = CMasterTemplate::MasterForGame(Game);
for (u32 iTemp = 0; iTemp < pMaster->NumScriptTemplates(); iTemp++)

View File

@ -1,9 +1,18 @@
#include "IInputStream.h"
#include <assert.h>
IInputStream::~IInputStream()
{
}
bool IInputStream::ReadBool()
{
char Val;
ReadBytes(&Val, 1);
assert(Val == 0 || Val == 1);
return (Val != 0 ? true : false);
}
char IInputStream::ReadByte()
{
char Val;

View File

@ -12,6 +12,7 @@ protected:
std::string mDataSource;
public:
bool ReadBool();
char ReadByte();
short ReadShort();
long ReadLong();

View File

@ -4,6 +4,12 @@ IOutputStream::~IOutputStream()
{
}
void IOutputStream::WriteBool(bool Val)
{
char ChrVal = (Val ? 1 : 0);
WriteBytes(&ChrVal, 1);
}
void IOutputStream::WriteByte(char Val)
{
WriteBytes(&Val, 1);

View File

@ -11,6 +11,7 @@ protected:
std::string mDataDest;
public:
void WriteBool(bool Val);
void WriteByte(char Val);
void WriteShort(short Val);
void WriteLong(long Val);

View File

@ -38,7 +38,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -267,7 +267,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -99,7 +99,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -209,7 +209,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -91,7 +91,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -145,7 +145,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -446,7 +446,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -193,7 +193,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">

View File

@ -434,7 +434,7 @@
<property ID="0x426F2F60">
<cook_pref>never</cook_pref>
</property>
<property ID="0x771A3176" extensions="CAUD">
<property ID="0x771A3176">
<cook_pref>never</cook_pref>
</property>
<property ID="0xEFD287D9">
@ -505,10 +505,10 @@
</property>
</properties>
</struct>
<property ID="0xE9C8E2BD" extensions="PART">
<property ID="0xE9C8E2BD">
<cook_pref>never</cook_pref>
</property>
<property ID="0xA3E8EC4E" extensions="CAUD">
<property ID="0xA3E8EC4E">
<cook_pref>never</cook_pref>
</property>
<property ID="0x053AE4A7">