Rewrote a chunk of the script cooker. SCGN script layer is now generated and written at cook time.

This commit is contained in:
Aruki 2017-06-27 06:13:42 -06:00
parent 2081835ae6
commit 4dc11023df
7 changed files with 233 additions and 226 deletions

View File

@ -185,31 +185,43 @@ void CAreaCooker::WriteAreaData(IOutputStream& rOut)
// ************ SCLY ************ // ************ SCLY ************
void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut) void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut)
{ {
rOut.WriteString("SCLY", 4); // This function covers both Prime 1 and the Echoes demo.
// The Echoes demo has a similar SCLY format but with minor layout differences and with SCGN.
rOut.WriteFourCC( FOURCC('SCLY') );
mVersion <= ePrime ? rOut.WriteLong(1) : rOut.WriteByte(1); mVersion <= ePrime ? rOut.WriteLong(1) : rOut.WriteByte(1);
u32 NumLayers = mpArea->mScriptLayers.size(); u32 NumLayers = mpArea->mScriptLayers.size();
rOut.WriteLong(NumLayers); rOut.WriteLong(NumLayers);
u32 LayerSizesStart = rOut.Tell(); u32 LayerSizesStart = rOut.Tell();
for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) for (u32 LayerIdx = 0; LayerIdx < NumLayers; LayerIdx++)
rOut.WriteLong(0); rOut.WriteLong(0);
// SCLY // SCLY
CScriptCooker ScriptCooker(mVersion, true);
std::vector<u32> LayerSizes(NumLayers); std::vector<u32> LayerSizes(NumLayers);
for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) for (u32 LayerIdx = 0; LayerIdx < NumLayers; LayerIdx++)
{ {
u32 LayerStart = rOut.Tell(); u32 LayerStart = rOut.Tell();
CScriptCooker::WriteLayer(mVersion, mpArea->mScriptLayers[iLyr], rOut); ScriptCooker.WriteLayer(rOut, mpArea->mScriptLayers[LayerIdx]);
LayerSizes[iLyr] = rOut.Tell() - LayerStart;
// Pad the layer to 32 bytes
u32 LayerSize = rOut.Tell() - LayerStart;
u32 PaddedSize = (LayerSize + 31) & ~31;
u32 NumPadBytes = PaddedSize - LayerSize;
for (u32 Pad = 0; Pad < NumPadBytes; Pad++)
rOut.WriteByte(0);
LayerSizes[LayerIdx] = PaddedSize;
} }
u32 LayersEnd = rOut.Tell(); u32 LayersEnd = rOut.Tell();
rOut.Seek(LayerSizesStart, SEEK_SET); rOut.Seek(LayerSizesStart, SEEK_SET);
for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) for (u32 LayerIdx = 0; LayerIdx < NumLayers; LayerIdx++)
rOut.WriteLong(LayerSizes[iLyr]); rOut.WriteLong(LayerSizes[LayerIdx]);
rOut.Seek(LayersEnd, SEEK_SET); rOut.Seek(LayersEnd, SEEK_SET);
FinishSection(false); FinishSection(false);
@ -217,31 +229,31 @@ void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut)
// SCGN // SCGN
if (mVersion == eEchoesDemo) if (mVersion == eEchoesDemo)
{ {
// IMPORTANT TODO - REGENERATE SCGN LAYER rOut.WriteFourCC( FOURCC('SCGN') );
/*rOut.WriteString("SCGN", 4);
rOut.WriteByte(1); rOut.WriteByte(1);
CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut); ScriptCooker.WriteGeneratedLayer(rOut);
FinishSection(false);*/ FinishSection(false);
} }
} }
void CAreaCooker::WriteEchoesSCLY(IOutputStream& rOut) void CAreaCooker::WriteEchoesSCLY(IOutputStream& rOut)
{ {
// SCLY // SCLY
for (u32 iLyr = 0; iLyr < mpArea->mScriptLayers.size(); iLyr++) CScriptCooker ScriptCooker(mVersion);
for (u32 LayerIdx = 0; LayerIdx < mpArea->mScriptLayers.size(); LayerIdx++)
{ {
rOut.WriteString("SCLY", 4); rOut.WriteFourCC( FOURCC('SCLY') );
rOut.WriteByte(0x1); rOut.WriteByte(1);
rOut.WriteLong(iLyr); rOut.WriteLong(LayerIdx);
CScriptCooker::WriteLayer(mVersion, mpArea->mScriptLayers[iLyr], rOut); ScriptCooker.WriteLayer(rOut, mpArea->mScriptLayers[LayerIdx]);
FinishSection(true); FinishSection(true);
} }
// SCGN // SCGN
// IMPORTANT TODO - REGENERATE SCGN rOut.WriteFourCC( FOURCC('SCGN') );
rOut.WriteString("SCGN", 4); rOut.WriteByte(1);
rOut.WriteByte(0x1); ScriptCooker.WriteGeneratedLayer(rOut);
//CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut);
FinishSection(true); FinishSection(true);
} }

View File

@ -1,16 +1,16 @@
#include "CScriptCooker.h" #include "CScriptCooker.h"
#include "Core/Resource/Script/CLink.h" #include "Core/Resource/Script/CLink.h"
void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) void CScriptCooker::WriteProperty(IOutputStream& rOut,IProperty *pProp, bool InSingleStruct)
{ {
u32 SizeOffset = 0, PropStart = 0; u32 SizeOffset = 0, PropStart = 0;
if (mVersion >= eEchoesDemo && !InSingleStruct) if (mGame >= eEchoesDemo && !InSingleStruct)
{ {
mpSCLY->WriteLong(pProp->ID()); rOut.WriteLong(pProp->ID());
SizeOffset = mpSCLY->Tell(); SizeOffset = rOut.Tell();
mpSCLY->WriteShort(0x0); rOut.WriteShort(0x0);
PropStart = mpSCLY->Tell(); PropStart = rOut.Tell();
} }
switch (pProp->Type()) switch (pProp->Type())
@ -19,91 +19,91 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct)
case eBoolProperty: case eBoolProperty:
{ {
TBoolProperty *pBoolCast = static_cast<TBoolProperty*>(pProp); TBoolProperty *pBoolCast = static_cast<TBoolProperty*>(pProp);
mpSCLY->WriteByte(pBoolCast->Get() ? 1 : 0); rOut.WriteBool(pBoolCast->Get());
break; break;
} }
case eByteProperty: case eByteProperty:
{ {
TByteProperty *pByteCast = static_cast<TByteProperty*>(pProp); TByteProperty *pByteCast = static_cast<TByteProperty*>(pProp);
mpSCLY->WriteByte(pByteCast->Get()); rOut.WriteByte(pByteCast->Get());
break; break;
} }
case eShortProperty: case eShortProperty:
{ {
TShortProperty *pShortCast = static_cast<TShortProperty*>(pProp); TShortProperty *pShortCast = static_cast<TShortProperty*>(pProp);
mpSCLY->WriteShort(pShortCast->Get()); rOut.WriteShort(pShortCast->Get());
break; break;
} }
case eLongProperty: case eLongProperty:
{ {
TLongProperty *pLongCast = static_cast<TLongProperty*>(pProp); TLongProperty *pLongCast = static_cast<TLongProperty*>(pProp);
mpSCLY->WriteLong(pLongCast->Get()); rOut.WriteLong(pLongCast->Get());
break; break;
} }
case eEnumProperty: case eEnumProperty:
{ {
TEnumProperty *pEnumCast = static_cast<TEnumProperty*>(pProp); TEnumProperty *pEnumCast = static_cast<TEnumProperty*>(pProp);
mpSCLY->WriteLong(pEnumCast->Get()); rOut.WriteLong(pEnumCast->Get());
break; break;
} }
case eBitfieldProperty: case eBitfieldProperty:
{ {
TBitfieldProperty *pBitfieldCast = static_cast<TBitfieldProperty*>(pProp); TBitfieldProperty *pBitfieldCast = static_cast<TBitfieldProperty*>(pProp);
mpSCLY->WriteLong(pBitfieldCast->Get()); rOut.WriteLong(pBitfieldCast->Get());
break; break;
} }
case eFloatProperty: case eFloatProperty:
{ {
TFloatProperty *pFloatCast = static_cast<TFloatProperty*>(pProp); TFloatProperty *pFloatCast = static_cast<TFloatProperty*>(pProp);
mpSCLY->WriteFloat(pFloatCast->Get()); rOut.WriteFloat(pFloatCast->Get());
break; break;
} }
case eStringProperty: case eStringProperty:
{ {
TStringProperty *pStringCast = static_cast<TStringProperty*>(pProp); TStringProperty *pStringCast = static_cast<TStringProperty*>(pProp);
mpSCLY->WriteString(pStringCast->Get()); rOut.WriteString(pStringCast->Get());
break; break;
} }
case eVector3Property: case eVector3Property:
{ {
TVector3Property *pVectorCast = static_cast<TVector3Property*>(pProp); TVector3Property *pVectorCast = static_cast<TVector3Property*>(pProp);
pVectorCast->Get().Write(*mpSCLY); pVectorCast->Get().Write(rOut);
break; break;
} }
case eColorProperty: case eColorProperty:
{ {
TColorProperty *pColorCast = static_cast<TColorProperty*>(pProp); TColorProperty *pColorCast = static_cast<TColorProperty*>(pProp);
pColorCast->Get().Write(*mpSCLY, false); pColorCast->Get().Write(rOut, false);
break; break;
} }
case eSoundProperty: case eSoundProperty:
{ {
TSoundProperty *pSoundCast = static_cast<TSoundProperty*>(pProp); TSoundProperty *pSoundCast = static_cast<TSoundProperty*>(pProp);
mpSCLY->WriteLong(pSoundCast->Get()); rOut.WriteLong(pSoundCast->Get());
break; break;
} }
case eAssetProperty: case eAssetProperty:
{ {
TAssetProperty *pAssetCast = static_cast<TAssetProperty*>(pProp); TAssetProperty *pAssetCast = static_cast<TAssetProperty*>(pProp);
pAssetCast->Get().Write(*mpSCLY); pAssetCast->Get().Write(rOut);
break; break;
} }
case eCharacterProperty: case eCharacterProperty:
{ {
TCharacterProperty *pCharCast = static_cast<TCharacterProperty*>(pProp); TCharacterProperty *pCharCast = static_cast<TCharacterProperty*>(pProp);
pCharCast->Get().Write(*mpSCLY); pCharCast->Get().Write(rOut);
break; break;
} }
@ -111,25 +111,25 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct)
{ {
TMayaSplineProperty *pSplineCast = static_cast<TMayaSplineProperty*>(pProp); TMayaSplineProperty *pSplineCast = static_cast<TMayaSplineProperty*>(pProp);
std::vector<u8> Buffer = pSplineCast->Get(); std::vector<u8> Buffer = pSplineCast->Get();
if (!Buffer.empty()) mpSCLY->WriteBytes(Buffer.data(), Buffer.size()); if (!Buffer.empty()) rOut.WriteBytes(Buffer.data(), Buffer.size());
else else
{ {
if (mVersion < eReturns) if (mGame < eReturns)
{ {
mpSCLY->WriteShort(0); rOut.WriteShort(0);
mpSCLY->WriteLong(0); rOut.WriteLong(0);
mpSCLY->WriteByte(1); rOut.WriteByte(1);
mpSCLY->WriteFloat(0); rOut.WriteFloat(0);
mpSCLY->WriteFloat(1); rOut.WriteFloat(1);
} }
else else
{ {
mpSCLY->WriteLong(0); rOut.WriteLong(0);
mpSCLY->WriteFloat(0); rOut.WriteFloat(0);
mpSCLY->WriteFloat(1); rOut.WriteFloat(1);
mpSCLY->WriteShort(0); rOut.WriteShort(0);
mpSCLY->WriteByte(1); rOut.WriteByte(1);
} }
} }
@ -153,14 +153,14 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct)
if (!pTemp->IsSingleProperty()) if (!pTemp->IsSingleProperty())
{ {
if (mVersion <= ePrime) if (mGame <= ePrime)
mpSCLY->WriteLong(PropertiesToWrite.size()); rOut.WriteLong(PropertiesToWrite.size());
else else
mpSCLY->WriteShort((u16) PropertiesToWrite.size()); rOut.WriteShort((u16) PropertiesToWrite.size());
} }
for (u32 iProp = 0; iProp < PropertiesToWrite.size(); iProp++) for (u32 iProp = 0; iProp < PropertiesToWrite.size(); iProp++)
WriteProperty(PropertiesToWrite[iProp], pTemp->IsSingleProperty()); WriteProperty(rOut, PropertiesToWrite[iProp], pTemp->IsSingleProperty());
break; break;
} }
@ -168,10 +168,10 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct)
case eArrayProperty: case eArrayProperty:
{ {
CArrayProperty *pArray = static_cast<CArrayProperty*>(pProp); CArrayProperty *pArray = static_cast<CArrayProperty*>(pProp);
mpSCLY->WriteLong(pArray->Count()); rOut.WriteLong(pArray->Count());
for (u32 iProp = 0; iProp < pArray->Count(); iProp++) for (u32 iProp = 0; iProp < pArray->Count(); iProp++)
WriteProperty(pArray->PropertyByIndex(iProp), true); WriteProperty(rOut, pArray->PropertyByIndex(iProp), true);
break; break;
} }
@ -180,133 +180,128 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct)
if (SizeOffset != 0) if (SizeOffset != 0)
{ {
u32 PropEnd = mpSCLY->Tell(); u32 PropEnd = rOut.Tell();
mpSCLY->Seek(SizeOffset, SEEK_SET); rOut.Seek(SizeOffset, SEEK_SET);
mpSCLY->WriteShort((u16) (PropEnd - PropStart)); rOut.WriteShort((u16) (PropEnd - PropStart));
mpSCLY->Seek(PropEnd, SEEK_SET); rOut.Seek(PropEnd, SEEK_SET);
} }
} }
void CScriptCooker::WriteLayerMP1(CScriptLayer *pLayer) // ************ PUBLIC ************
void CScriptCooker::WriteInstance(IOutputStream& rOut, CScriptObject *pInstance)
{ {
u32 LayerStart = mpSCLY->Tell(); ASSERT(pInstance->Area()->Game() == mGame);
mpSCLY->WriteByte(0); // Unknown value
mpSCLY->WriteLong(pLayer->NumInstances());
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++) // Note the format is pretty much the same between games; the main difference is a
{ // number of fields changed size between MP1 and 2, but they're still the same fields
CScriptObject *pInstance = pLayer->InstanceByIndex(iInst); bool IsPrime1 = (mGame <= ePrime);
WriteInstanceMP1(pInstance);
}
u32 LayerSize = mpSCLY->Tell() - LayerStart; u32 ObjectType = pInstance->ObjectTypeID();
u32 NumPadBytes = 32 - (LayerSize % 32); IsPrime1 ? rOut.WriteByte((u8) ObjectType) : rOut.WriteLong(ObjectType);
if (NumPadBytes == 32) NumPadBytes = 0;
for (u32 iPad = 0; iPad < NumPadBytes; iPad++) u32 SizeOffset = rOut.Tell();
mpSCLY->WriteByte(0); IsPrime1 ? rOut.WriteLong(0) : rOut.WriteShort(0);
}
void CScriptCooker::WriteInstanceMP1(CScriptObject *pInstance)
{
mpSCLY->WriteByte((u8) pInstance->ObjectTypeID());
u32 SizeOffset = mpSCLY->Tell();
mpSCLY->WriteLong(0);
u32 InstanceStart = mpSCLY->Tell();
u32 InstanceStart = rOut.Tell();
u32 InstanceID = (pInstance->Layer()->AreaIndex() << 26) | pInstance->InstanceID(); u32 InstanceID = (pInstance->Layer()->AreaIndex() << 26) | pInstance->InstanceID();
mpSCLY->WriteLong(InstanceID); rOut.WriteLong(InstanceID);
mpSCLY->WriteLong(pInstance->NumLinks(eOutgoing));
u32 NumLinks = pInstance->NumLinks(eOutgoing);
IsPrime1 ? rOut.WriteLong(NumLinks) : rOut.WriteShort((u16) NumLinks);
for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++) for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++)
{ {
CLink *pLink = pInstance->Link(eOutgoing, iLink); CLink *pLink = pInstance->Link(eOutgoing, iLink);
mpSCLY->WriteLong(pLink->State()); rOut.WriteLong(pLink->State());
mpSCLY->WriteLong(pLink->Message()); rOut.WriteLong(pLink->Message());
mpSCLY->WriteLong(pLink->ReceiverID()); rOut.WriteLong(pLink->ReceiverID());
} }
WriteProperty(pInstance->Properties(), false); WriteProperty(rOut, pInstance->Properties(), false);
u32 InstanceEnd = mpSCLY->Tell(); u32 InstanceEnd = rOut.Tell();
mpSCLY->Seek(SizeOffset, SEEK_SET); rOut.Seek(SizeOffset, SEEK_SET);
mpSCLY->WriteLong(InstanceEnd - InstanceStart); u32 Size = InstanceEnd - InstanceStart;
mpSCLY->Seek(InstanceEnd, SEEK_SET); IsPrime1 ? rOut.WriteLong(Size) : rOut.WriteShort((u16) Size);
rOut.Seek(InstanceEnd, SEEK_SET);
} }
void CScriptCooker::WriteLayerMP2(CScriptLayer *pLayer) void CScriptCooker::WriteLayer(IOutputStream& rOut, CScriptLayer *pLayer)
{ {
u32 LayerStart = mpSCLY->Tell(); ASSERT(pLayer->Area()->Game() == mGame);
mpSCLY->WriteByte(0x1);
mpSCLY->WriteLong(pLayer->NumInstances()); rOut.WriteByte( mGame <= ePrime ? 0 : 1 ); // Version
u32 InstanceCountOffset = rOut.Tell();
u32 NumWrittenInstances = 0;
rOut.WriteLong(0);
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++) for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++)
{ {
CScriptObject *pInstance = pLayer->InstanceByIndex(iInst); CScriptObject *pInstance = pLayer->InstanceByIndex(iInst);
WriteInstanceMP2(pInstance);
// Is this a generated instance?
bool ShouldWrite = true;
if (mWriteGeneratedSeparately)
{
// GenericCreature instances in DKCR always write to both SCLY and SCGN
if (mGame == eReturns && pInstance->ObjectTypeID() == FOURCC('GCTR'))
mGeneratedObjects.push_back(pInstance);
// Instances receiving a Generate/Activate message (MP2) or a
// Generate/Attach message (MP3+) should be written to SCGN, not SCLY
else
{
for (u32 LinkIdx = 0; LinkIdx < pInstance->NumLinks(eIncoming); LinkIdx++)
{
CLink *pLink = pInstance->Link(eIncoming, LinkIdx);
if (mGame <= eEchoes)
{
if (pLink->State() == FOURCC('GRNT') && pLink->Message() == FOURCC('ACTV'))
{
ShouldWrite = false;
break;
}
}
else
{
if (pLink->Message() == FOURCC('ATCH'))
{
if (pLink->State() == FOURCC('GRNT') || pLink->State() == FOURCC('GRN0') || pLink->State() == FOURCC('GRN1'))
{
ShouldWrite = false;
break;
}
}
}
}
if (!ShouldWrite)
mGeneratedObjects.push_back(pInstance);
}
}
if (ShouldWrite)
{
WriteInstance(rOut, pInstance);
NumWrittenInstances++;
}
} }
if (mVersion == eEchoesDemo) u32 LayerEnd = rOut.Tell();
{ rOut.GoTo(InstanceCountOffset);
u32 LayerSize = mpSCLY->Tell() - LayerStart; rOut.WriteLong(NumWrittenInstances);
u32 NumPadBytes = 32 - (LayerSize % 32); rOut.GoTo(LayerEnd);
if (NumPadBytes == 32) NumPadBytes = 0;
for (u32 iPad = 0; iPad < NumPadBytes; iPad++)
mpSCLY->WriteByte(0);
}
} }
void CScriptCooker::WriteInstanceMP2(CScriptObject *pInstance) void CScriptCooker::WriteGeneratedLayer(IOutputStream& rOut)
{ {
mpSCLY->WriteLong(pInstance->ObjectTypeID()); rOut.WriteByte(1); // Version
rOut.WriteLong(mGeneratedObjects.size());
u32 SizeOffset = mpSCLY->Tell(); for (u32 InstIdx = 0; InstIdx < mGeneratedObjects.size(); InstIdx++)
mpSCLY->WriteShort(0); WriteInstance(rOut, mGeneratedObjects[InstIdx]);
u32 InstanceStart = mpSCLY->Tell();
u32 InstanceID = (pInstance->Layer()->AreaIndex() << 26) | pInstance->InstanceID();
mpSCLY->WriteLong(InstanceID);
mpSCLY->WriteShort((u16) pInstance->NumLinks(eOutgoing));
for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++)
{
CLink *pLink = pInstance->Link(eOutgoing, iLink);
mpSCLY->WriteLong(pLink->State());
mpSCLY->WriteLong(pLink->Message());
mpSCLY->WriteLong(pLink->ReceiverID());
}
WriteProperty(pInstance->Properties(), false);
u32 InstanceEnd = mpSCLY->Tell();
mpSCLY->Seek(SizeOffset, SEEK_SET);
mpSCLY->WriteShort((u16) (InstanceEnd - InstanceStart));
mpSCLY->Seek(InstanceEnd, SEEK_SET);
}
// ************ STATIC ************
void CScriptCooker::WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut)
{
CScriptCooker Cooker;
Cooker.mpSCLY = &rOut;
Cooker.mVersion = Game;
if (Game <= ePrime)
Cooker.WriteLayerMP1(pLayer);
else
Cooker.WriteLayerMP2(pLayer);
}
void CScriptCooker::CookInstance(EGame Game, CScriptObject *pInstance, IOutputStream& rOut)
{
CScriptCooker Cooker;
Cooker.mpSCLY = &rOut;
Cooker.mVersion = Game;
if (Game <= ePrime)
Cooker.WriteInstanceMP1(pInstance);
else
Cooker.WriteInstanceMP2(pInstance);
} }

View File

@ -1,6 +1,7 @@
#ifndef CSCRIPTCOOKER_H #ifndef CSCRIPTCOOKER_H
#define CSCRIPTCOOKER_H #define CSCRIPTCOOKER_H
#include "CSectionMgrOut.h"
#include "Core/Resource/Script/CScriptLayer.h" #include "Core/Resource/Script/CScriptLayer.h"
#include "Core/Resource/Script/CScriptObject.h" #include "Core/Resource/Script/CScriptObject.h"
#include <Common/EGame.h> #include <Common/EGame.h>
@ -8,19 +9,21 @@
class CScriptCooker class CScriptCooker
{ {
IOutputStream *mpSCLY; EGame mGame;
EGame mVersion; std::vector<CScriptObject*> mGeneratedObjects;
bool mWriteGeneratedSeparately;
CScriptCooker() {} void WriteProperty(IOutputStream& rOut,IProperty *pProp, bool InSingleStruct);
void WriteProperty(IProperty *pProp, bool InSingleStruct);
void WriteLayerMP1(CScriptLayer *pLayer);
void WriteInstanceMP1(CScriptObject *pInstance);
void WriteLayerMP2(CScriptLayer *pLayer);
void WriteInstanceMP2(CScriptObject *pInstance);
public: public:
static void WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut); CScriptCooker(EGame Game, bool WriteGeneratedObjectsSeparately = true)
static void CookInstance(EGame Game, CScriptObject *pInstance, IOutputStream& rOut); : mGame(Game)
, mWriteGeneratedSeparately(WriteGeneratedObjectsSeparately && mGame >= eEchoesDemo)
{}
void WriteInstance(IOutputStream& rOut, CScriptObject *pInstance);
void WriteLayer(IOutputStream& rOut, CScriptLayer *pLayer);
void WriteGeneratedLayer(IOutputStream& rOut);
}; };
#endif // CSCRIPTCOOKER_H #endif // CSCRIPTCOOKER_H

View File

@ -143,28 +143,25 @@ void CAreaLoader::ReadSCLYPrime()
} }
// SCGN // SCGN
if (mVersion == eEchoesDemo) CScriptLayer *pGenLayer = nullptr;
if (mVersion >= eEchoesDemo)
{ {
mpSectionMgr->ToSection(mScriptGeneratorBlockNum); mpSectionMgr->ToSection(mScriptGeneratorBlockNum);
CFourCC SCGN = mpMREA->ReadFourCC();
CFourCC SCGN(*mpMREA); if (SCGN != FOURCC('SCGN'))
if (SCGN != "SCGN")
Log::FileError(mpMREA->GetSourceString(), mpMREA->Tell() - 4, "Invalid SCGN magic: " + SCGN.ToString()); Log::FileError(mpMREA->GetSourceString(), mpMREA->Tell() - 4, "Invalid SCGN magic: " + SCGN.ToString());
else else
{ {
mpMREA->Seek(0x1, SEEK_CUR); mpMREA->Seek(0x1, SEEK_CUR);
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); pGenLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
if (pLayer)
{
MergeGeneratedLayer(pLayer);
delete pLayer;
}
} }
} }
SetUpObjects(); SetUpObjects(pGenLayer);
delete pGenLayer;
} }
void CAreaLoader::ReadLightsPrime() void CAreaLoader::ReadLightsPrime()
@ -310,7 +307,6 @@ void CAreaLoader::ReadSCLYEchoes()
} }
// SCGN // SCGN
// we want to regenerate the SCGN layer on cook - for now just move everything back to its original layer
CFourCC SCGN(*mpMREA); CFourCC SCGN(*mpMREA);
if (SCGN != "SCGN") if (SCGN != "SCGN")
{ {
@ -320,14 +316,8 @@ void CAreaLoader::ReadSCLYEchoes()
mpMREA->Seek(0x1, SEEK_CUR); // Skipping unknown mpMREA->Seek(0x1, SEEK_CUR); // Skipping unknown
CScriptLayer *pGeneratedLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); CScriptLayer *pGeneratedLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
SetUpObjects(pGeneratedLayer);
if (pGeneratedLayer) delete pGeneratedLayer;
{
MergeGeneratedLayer(pGeneratedLayer);
delete pGeneratedLayer;
}
SetUpObjects();
} }
// ************ CORRUPTION ************ // ************ CORRUPTION ************
@ -606,61 +596,66 @@ void CAreaLoader::ReadEGMC()
mpArea->mpPoiToWorldMap = gpResourceStore->LoadResource(EGMC, eStaticGeometryMap); mpArea->mpPoiToWorldMap = gpResourceStore->LoadResource(EGMC, eStaticGeometryMap);
} }
void CAreaLoader::MergeGeneratedLayer(CScriptLayer *pLayer) void CAreaLoader::SetUpObjects(CScriptLayer *pGenLayer)
{ {
while (pLayer->NumInstances() != 0) // Create instance map
for (u32 LayerIdx = 0; LayerIdx < mpArea->NumScriptLayers(); LayerIdx++)
{ {
CScriptObject *pObj = pLayer->InstanceByIndex(0); CScriptLayer *pLayer = mpArea->mScriptLayers[LayerIdx];
u32 InstanceID = pObj->InstanceID();
for (u32 InstIdx = 0; InstIdx < pLayer->NumInstances(); InstIdx++)
{
CScriptObject *pInst = pLayer->InstanceByIndex(InstIdx);
u32 InstanceID = pInst->InstanceID();
CScriptObject *pExisting = mpArea->InstanceByID(InstanceID);
ASSERT(pExisting == nullptr);
mpArea->mObjectMap[InstanceID] = pInst;
}
}
// Merge objects from the generated layer back into the regular script layers
while (pGenLayer->NumInstances() != 0)
{
CScriptObject *pInst = pGenLayer->InstanceByIndex(0);
u32 InstanceID = pInst->InstanceID();
// Check if this is a duplicate of an existing instance (this only happens with DKCR GenericCreature as far as I'm aware) // Check if this is a duplicate of an existing instance (this only happens with DKCR GenericCreature as far as I'm aware)
CScriptObject *pDupe = mpArea->InstanceByID(InstanceID); if (mpArea->InstanceByID(InstanceID) != nullptr)
if (pDupe)
{ {
Log::Write("Duplicate SCGN object: [" + pObj->Template()->Name() + "] " + pObj->InstanceName() + " (" + TString::HexString(pObj->InstanceID(), 8, false) + ")"); Log::Write("Duplicate SCGN object: [" + pInst->Template()->Name() + "] " + pInst->InstanceName() + " (" + TString::HexString(pInst->InstanceID(), 8, false) + ")");
pLayer->RemoveInstance(pObj); pGenLayer->RemoveInstance(pInst);
delete pObj; delete pInst;
} }
else else
{ {
u32 LayerIdx = (InstanceID >> 26) & 0x3F; u32 LayerIdx = (InstanceID >> 26) & 0x3F;
pObj->SetLayer( mpArea->ScriptLayer(LayerIdx) ); pInst->SetLayer( mpArea->ScriptLayer(LayerIdx) );
mpArea->mObjectMap[InstanceID] = pInst;
} }
} }
}
void CAreaLoader::SetUpObjects()
{
// Iterate over all objects // Iterate over all objects
for (u32 iLyr = 0; iLyr < mpArea->NumScriptLayers(); iLyr++) for (auto Iter = mpArea->mObjectMap.begin(); Iter != mpArea->mObjectMap.end(); Iter++)
{ {
CScriptLayer *pLayer = mpArea->mScriptLayers[iLyr]; CScriptObject *pInst = Iter->second;
for (u32 iObj = 0; iObj < pLayer->NumInstances(); iObj++) // Store outgoing connections
for (u32 iCon = 0; iCon < pInst->NumLinks(eOutgoing); iCon++)
{ {
// Add object to object map CLink *pLink = pInst->Link(eOutgoing, iCon);
CScriptObject *pObj = (*pLayer)[iObj]; mConnectionMap[pLink->ReceiverID()].push_back(pLink);
mpArea->mObjectMap[pObj->InstanceID()] = pObj; }
// Store outgoing connections // Remove "-component" garbage from MP1 instance names
for (u32 iCon = 0; iCon < pObj->NumLinks(eOutgoing); iCon++) if (mVersion <= ePrime)
{ {
CLink *pLink = pObj->Link(eOutgoing, iCon); TString InstanceName = pInst->InstanceName();
mConnectionMap[pLink->ReceiverID()].push_back(pLink);
}
// Remove "-component" garbage from MP1 instance names while (InstanceName.EndsWith("-component"))
if (mVersion <= ePrime) InstanceName = InstanceName.ChopBack(10);
{
TString InstanceName = pObj->InstanceName();
while (InstanceName.EndsWith("-component")) pInst->SetName(InstanceName);
InstanceName = InstanceName.ChopBack(10);
pObj->SetName(InstanceName);
}
} }
} }

View File

@ -77,8 +77,7 @@ class CAreaLoader
void ReadPATH(); void ReadPATH();
void ReadPTLA(); void ReadPTLA();
void ReadEGMC(); void ReadEGMC();
void MergeGeneratedLayer(CScriptLayer *pLayer); void SetUpObjects(CScriptLayer *pGenLayer);
void SetUpObjects();
public: public:
static CGameArea* LoadMREA(IInputStream& rMREA, CResourceEntry *pEntry); static CGameArea* LoadMREA(IInputStream& rMREA, CResourceEntry *pEntry);

View File

@ -70,7 +70,9 @@ public:
rNode.OriginalInstanceID = pInst->InstanceID(); rNode.OriginalInstanceID = pInst->InstanceID();
CVectorOutStream Out(&rNode.InstanceData, IOUtil::eBigEndian); CVectorOutStream Out(&rNode.InstanceData, IOUtil::eBigEndian);
CScriptCooker::CookInstance(eReturns, static_cast<CScriptNode*>(*It)->Instance(), Out);
CScriptCooker Cooker(mGame);
Cooker.WriteInstance(Out, static_cast<CScriptNode*>(*It)->Instance());
// Replace instance ID with 0xFFFFFFFF to force it to generate a new one. // Replace instance ID with 0xFFFFFFFF to force it to generate a new one.
Out.Seek(0x6, SEEK_SET); Out.Seek(0x6, SEEK_SET);

View File

@ -64,7 +64,8 @@ CDeleteSelectionCommand::CDeleteSelectionCommand(CWorldEditor *pEditor, const QS
} }
CVectorOutStream PropertyDataOut(&rNode.InstanceData, IOUtil::eBigEndian); CVectorOutStream PropertyDataOut(&rNode.InstanceData, IOUtil::eBigEndian);
CScriptCooker::CookInstance(eReturns, pInst, PropertyDataOut); CScriptCooker Cooker(pEditor->CurrentGame());
Cooker.WriteInstance(PropertyDataOut, pInst);
} }
else else