Added support for Prime 3 package cooking
This commit is contained in:
parent
560706d285
commit
7f18a33fae
|
@ -226,7 +226,8 @@ HEADERS += \
|
|||
Resource/CAudioMacro.h \
|
||||
CompressionUtil.h \
|
||||
Resource/Animation/CSourceAnimData.h \
|
||||
Resource/CMapArea.h
|
||||
Resource/CMapArea.h \
|
||||
Resource/CSavedStateID.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
|
|
@ -77,48 +77,94 @@ void CPackage::Cook()
|
|||
return;
|
||||
}
|
||||
|
||||
// todo: MP3/DKCR pak format support
|
||||
Pak.WriteLong(0x00030005); // Major/Minor Version
|
||||
Pak.WriteLong(0); // Unknown
|
||||
EGame Game = mpProject->Game();
|
||||
u32 Alignment = (Game <= eCorruptionProto ? 0x20 : 0x40);
|
||||
u32 AlignmentMinusOne = Alignment - 1;
|
||||
|
||||
// Named Resources
|
||||
Pak.WriteLong(mResources.size());
|
||||
u32 TocOffset = 0;
|
||||
u32 NamesSize = 0;
|
||||
u32 ResTableOffset = 0;
|
||||
u32 ResTableSize = 0;
|
||||
u32 ResDataSize = 0;
|
||||
|
||||
for (auto Iter = mResources.begin(); Iter != mResources.end(); Iter++)
|
||||
// Write MP1 pak header
|
||||
if (Game <= eCorruptionProto)
|
||||
{
|
||||
const SNamedResource& rkRes = *Iter;
|
||||
rkRes.Type.Write(Pak);
|
||||
rkRes.ID.Write(Pak);
|
||||
Pak.WriteSizedString(rkRes.Name);
|
||||
Pak.WriteLong(0x00030005); // Major/Minor Version
|
||||
Pak.WriteLong(0); // Unknown
|
||||
|
||||
// Named Resources
|
||||
Pak.WriteLong(mResources.size());
|
||||
|
||||
for (auto Iter = mResources.begin(); Iter != mResources.end(); Iter++)
|
||||
{
|
||||
const SNamedResource& rkRes = *Iter;
|
||||
rkRes.Type.Write(Pak);
|
||||
rkRes.ID.Write(Pak);
|
||||
Pak.WriteSizedString(rkRes.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in table of contents with junk, write later
|
||||
// Write MP3 pak header
|
||||
else
|
||||
{
|
||||
// Header
|
||||
Pak.WriteLong(2); // Version
|
||||
Pak.WriteLong(0x40); // Header size
|
||||
Pak.WriteToBoundary(0x40, 0); // We don't care about the MD5 hash; the game doesn't use it
|
||||
|
||||
// PAK table of contents; write later
|
||||
TocOffset = Pak.Tell();
|
||||
Pak.WriteLong(0);
|
||||
Pak.WriteToBoundary(0x40, 0);
|
||||
|
||||
// Named Resources
|
||||
u32 NamesStart = Pak.Tell();
|
||||
Pak.WriteLong(mResources.size());
|
||||
|
||||
for (auto Iter = mResources.begin(); Iter != mResources.end(); Iter++)
|
||||
{
|
||||
const SNamedResource& rkRes = *Iter;
|
||||
Pak.WriteString(rkRes.Name);
|
||||
rkRes.Type.Write(Pak);
|
||||
rkRes.ID.Write(Pak);
|
||||
}
|
||||
|
||||
Pak.WriteToBoundary(0x40, 0);
|
||||
NamesSize = Pak.Tell() - NamesStart;
|
||||
}
|
||||
|
||||
// Fill in resource table with junk, write later
|
||||
ResTableOffset = Pak.Tell();
|
||||
Pak.WriteLong(AssetList.size());
|
||||
u32 TocOffset = Pak.Tell();
|
||||
CAssetID Dummy = CAssetID::InvalidID(Game);
|
||||
|
||||
for (u32 iRes = 0; iRes < AssetList.size(); iRes++)
|
||||
{
|
||||
Pak.WriteLongLong(0);
|
||||
Dummy.Write(Pak);
|
||||
Pak.WriteLongLong(0);
|
||||
Pak.WriteLong(0);
|
||||
}
|
||||
|
||||
Pak.WriteToBoundary(32, 0);
|
||||
Pak.WriteToBoundary(Alignment, 0);
|
||||
ResTableSize = Pak.Tell() - ResTableOffset;
|
||||
|
||||
// Start writing resources
|
||||
struct SResourceTocInfo
|
||||
struct SResourceTableInfo
|
||||
{
|
||||
CResourceEntry *pEntry;
|
||||
u32 Offset;
|
||||
u32 Size;
|
||||
bool Compressed;
|
||||
};
|
||||
std::vector<SResourceTocInfo> ResourceTocData(AssetList.size());
|
||||
std::vector<SResourceTableInfo> ResourceTableData(AssetList.size());
|
||||
u32 ResIdx = 0;
|
||||
u32 ResDataOffset = Pak.Tell();
|
||||
|
||||
for (auto Iter = AssetList.begin(); Iter != AssetList.end(); Iter++, ResIdx++)
|
||||
{
|
||||
// Initialize entry, recook assets if needed
|
||||
u32 AssetOffset = Pak.Tell();
|
||||
CAssetID ID = *Iter;
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(ID);
|
||||
ASSERT(pEntry != nullptr);
|
||||
|
@ -126,9 +172,9 @@ void CPackage::Cook()
|
|||
if (pEntry->NeedsRecook())
|
||||
pEntry->Cook();
|
||||
|
||||
SResourceTocInfo& rTocInfo = ResourceTocData[ResIdx];
|
||||
rTocInfo.pEntry = pEntry;
|
||||
rTocInfo.Offset = Pak.Tell();
|
||||
SResourceTableInfo& rTableInfo = ResourceTableData[ResIdx];
|
||||
rTableInfo.pEntry = pEntry;
|
||||
rTableInfo.Offset = (Game <= eEchoes ? AssetOffset : AssetOffset - ResDataOffset);
|
||||
|
||||
// Load resource data
|
||||
CFileInStream CookedAsset(pEntry->CookedAssetPath(), IOUtil::eBigEndian);
|
||||
|
@ -141,21 +187,31 @@ void CPackage::Cook()
|
|||
// Check if this asset should be compressed; there are a few resource types that are
|
||||
// always compressed, and some types that are compressed if they're over a certain size
|
||||
EResType Type = pEntry->ResourceType();
|
||||
u32 CompressThreshold = (Game <= eCorruptionProto ? 0x400 : 0x80);
|
||||
|
||||
bool ShouldAlwaysCompress = (Type == eTexture || Type == eModel || Type == eSkin ||
|
||||
Type == eAnimSet || Type == eAnimation || Type == eFont);
|
||||
|
||||
if (Game >= eCorruption)
|
||||
{
|
||||
ShouldAlwaysCompress = ShouldAlwaysCompress ||
|
||||
(Type == eCharacter || Type == eSourceAnimData || Type == eScan ||
|
||||
Type == eAudioSample || Type == eStringTable || Type == eAudioAmplitudeData ||
|
||||
Type == eDynamicCollision);
|
||||
}
|
||||
|
||||
bool ShouldCompressConditional = !ShouldAlwaysCompress &&
|
||||
(Type == eParticle || Type == eParticleElectric || Type == eParticleSwoosh ||
|
||||
Type == eParticleWeapon || Type == eParticleDecal || Type == eParticleCollisionResponse);
|
||||
Type == eParticleWeapon || Type == eParticleDecal || Type == eParticleCollisionResponse ||
|
||||
Type == eParticleSpawn || Type == eParticleSorted || Type == eBurstFireData);
|
||||
|
||||
bool ShouldCompress = ShouldAlwaysCompress || (ShouldCompressConditional && ResourceSize >= 0x400);
|
||||
bool ShouldCompress = ShouldAlwaysCompress || (ShouldCompressConditional && ResourceSize >= CompressThreshold);
|
||||
|
||||
// Write resource data to pak
|
||||
if (!ShouldCompress)
|
||||
{
|
||||
Pak.WriteBytes(ResourceData.data(), ResourceSize);
|
||||
rTocInfo.Compressed = false;
|
||||
rTableInfo.Compressed = false;
|
||||
}
|
||||
|
||||
else
|
||||
|
@ -164,7 +220,7 @@ void CPackage::Cook()
|
|||
std::vector<u8> CompressedData(ResourceData.size() * 2);
|
||||
bool Success = false;
|
||||
|
||||
if (mpProject->Game() <= eEchoesDemo)
|
||||
if (Game <= eEchoesDemo || Game == eReturns)
|
||||
Success = CompressionUtil::CompressZlib(ResourceData.data(), ResourceData.size(), CompressedData.data(), CompressedData.size(), CompressedSize);
|
||||
else
|
||||
Success = CompressionUtil::CompressLZOSegmented(ResourceData.data(), ResourceData.size(), CompressedData.data(), CompressedSize, false);
|
||||
|
@ -172,44 +228,77 @@ void CPackage::Cook()
|
|||
// Make sure that the compressed data is actually smaller, accounting for padding + uncompressed size value
|
||||
if (Success)
|
||||
{
|
||||
u32 PaddedUncompressedSize = (ResourceSize + 0x1F) & ~0x1F;
|
||||
u32 PaddedCompressedSize = (CompressedSize + 4 + 0x1F) & ~0x1F;
|
||||
u32 CompressionHeaderSize = (Game <= eCorruptionProto ? 4 : 0x10);
|
||||
u32 PaddedUncompressedSize = (ResourceSize + AlignmentMinusOne) & ~AlignmentMinusOne;
|
||||
u32 PaddedCompressedSize = (CompressedSize + CompressionHeaderSize + AlignmentMinusOne) & ~AlignmentMinusOne;
|
||||
Success = (PaddedCompressedSize < PaddedUncompressedSize);
|
||||
}
|
||||
|
||||
// Write file to pak
|
||||
if (Success)
|
||||
{
|
||||
Pak.WriteLong(ResourceSize);
|
||||
// Write MP1/2 compressed asset
|
||||
if (Game <= eCorruptionProto)
|
||||
{
|
||||
Pak.WriteLong(ResourceSize);
|
||||
}
|
||||
// Write MP3/DKCR compressed asset
|
||||
else
|
||||
{
|
||||
// Note: Compressed asset data can be stored in multiple blocks. Normally, the only assets that make use of this are textures,
|
||||
// which can store each separate component of the file (header, palette, image data) in separate blocks. However, some textures
|
||||
// are stored in one block, and I've had no luck figuring out why. The game doesn't generally seem to care whether textures use
|
||||
// multiple blocks or not, so for the sake of complicity we compress everything to one block.
|
||||
Pak.WriteFourCC( FOURCC('CMPD') );
|
||||
Pak.WriteLong(1);
|
||||
Pak.WriteLong(0xA0000000 | CompressedSize);
|
||||
Pak.WriteLong(ResourceSize);
|
||||
}
|
||||
Pak.WriteBytes(CompressedData.data(), CompressedSize);
|
||||
}
|
||||
else
|
||||
Pak.WriteBytes(ResourceData.data(), ResourceSize);
|
||||
|
||||
rTocInfo.Compressed = Success;
|
||||
rTableInfo.Compressed = Success;
|
||||
}
|
||||
|
||||
Pak.WriteToBoundary(32, 0xFF);
|
||||
rTocInfo.Size = Pak.Tell() - rTocInfo.Offset;
|
||||
Pak.WriteToBoundary(Alignment, 0xFF);
|
||||
rTableInfo.Size = Pak.Tell() - AssetOffset;
|
||||
}
|
||||
ResDataSize = Pak.Tell() - ResDataOffset;
|
||||
|
||||
// Write table of contents for real
|
||||
Pak.Seek(TocOffset, SEEK_SET);
|
||||
if (Game >= eCorruption)
|
||||
{
|
||||
Pak.Seek(TocOffset, SEEK_SET);
|
||||
Pak.WriteLong(3); // Always 3 pak sections
|
||||
Pak.WriteFourCC( FOURCC('STRG') );
|
||||
Pak.WriteLong(NamesSize);
|
||||
Pak.WriteFourCC( FOURCC('RSHD') );
|
||||
Pak.WriteLong(ResTableSize);
|
||||
Pak.WriteFourCC( FOURCC('DATA') );
|
||||
Pak.WriteLong(ResDataSize);
|
||||
}
|
||||
|
||||
// Write resource table for real
|
||||
Pak.Seek(ResTableOffset+4, SEEK_SET);
|
||||
|
||||
for (u32 iRes = 0; iRes < AssetList.size(); iRes++)
|
||||
{
|
||||
const SResourceTocInfo& rkTocInfo = ResourceTocData[iRes];
|
||||
CResourceEntry *pEntry = rkTocInfo.pEntry;
|
||||
const SResourceTableInfo& rkInfo = ResourceTableData[iRes];
|
||||
CResourceEntry *pEntry = rkInfo.pEntry;
|
||||
|
||||
Pak.WriteLong( rkTocInfo.Compressed ? 1 : 0 );
|
||||
Pak.WriteLong( rkInfo.Compressed ? 1 : 0 );
|
||||
pEntry->CookedExtension().Write(Pak);
|
||||
pEntry->ID().Write(Pak);
|
||||
Pak.WriteLong(rkTocInfo.Size);
|
||||
Pak.WriteLong(rkTocInfo.Offset);
|
||||
Pak.WriteLong(rkInfo.Size);
|
||||
Pak.WriteLong(rkInfo.Offset);
|
||||
}
|
||||
|
||||
// Clear recook flag
|
||||
mNeedsRecook = false;
|
||||
Save();
|
||||
|
||||
Log::Write("Finished writing " + PakPath);
|
||||
|
||||
// Update resource store in case we recooked any assets
|
||||
|
|
|
@ -461,6 +461,10 @@ void CAreaDependencyListBuilder::AddDependency(const CAssetID& rkID, std::list<C
|
|||
return;
|
||||
}
|
||||
|
||||
// If this is an audio stream, skip
|
||||
if (ResType == eStreamedAudio)
|
||||
return;
|
||||
|
||||
// Check to ensure this is a valid/new dependency
|
||||
if (ResType == eWorld || ResType == eArea)
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef CSAVEDSTATEID_H
|
||||
#define CSAVEDSTATEID_H
|
||||
|
||||
#include <Common/Common.h>
|
||||
|
||||
// GUID representing a value stored in the save file for MP3/DKCR
|
||||
class CSavedStateID
|
||||
{
|
||||
u64 m[2];
|
||||
|
||||
public:
|
||||
CSavedStateID()
|
||||
{
|
||||
m[0] = 0;
|
||||
m[1] = 0;
|
||||
}
|
||||
|
||||
CSavedStateID(u64 Part1, u64 Part2)
|
||||
{
|
||||
m[0] = Part1;
|
||||
m[1] = Part2;
|
||||
}
|
||||
|
||||
CSavedStateID(IInputStream& rInput)
|
||||
{
|
||||
m[0] = rInput.ReadLongLong();
|
||||
m[1] = rInput.ReadLongLong();
|
||||
}
|
||||
|
||||
TString ToString()
|
||||
{
|
||||
u32 Part1 = (m[0] >> 32) & 0xFFFFFFFF;
|
||||
u32 Part2 = (m[0] >> 16) & 0x0000FFFF;
|
||||
u32 Part3 = (m[0] >> 00) & 0x0000FFFF;
|
||||
u32 Part4 = (m[1] >> 48) & 0x0000FFFF;
|
||||
u32 Part5 = (m[1] >> 32) & 0x0000FFFF;
|
||||
u32 Part6 = (m[1] >> 00) & 0xFFFFFFFF;
|
||||
return TString::Format("%08X-%04X-%04X-%04X-%04X%08X", Part1, Part2, Part3, Part4, Part5, Part6);
|
||||
}
|
||||
|
||||
void Write(IOutputStream& rOutput)
|
||||
{
|
||||
rOutput.WriteLongLong(m[0]);
|
||||
rOutput.WriteLongLong(m[1]);
|
||||
}
|
||||
|
||||
void Serialize(IArchive& rArc)
|
||||
{
|
||||
TString Str;
|
||||
if (rArc.IsWriter()) Str = ToString();
|
||||
rArc.SerializePrimitive(Str);
|
||||
if (rArc.IsReader()) *this = FromString(Str);
|
||||
}
|
||||
|
||||
// Operators
|
||||
inline bool operator==(const CSavedStateID& rkOther)
|
||||
{
|
||||
return (m[0] == rkOther.m[0] && m[1] == rkOther.m[1]);
|
||||
}
|
||||
|
||||
inline bool operator!=(const CSavedStateID& rkOther)
|
||||
{
|
||||
return !(*this == rkOther);
|
||||
}
|
||||
|
||||
inline bool operator<(const CSavedStateID& rkOther)
|
||||
{
|
||||
return (m[0] == rkOther.m[0] ? m[1] < rkOther.m[1] : m[0] < rkOther.m[0]);
|
||||
}
|
||||
|
||||
// Static
|
||||
static CSavedStateID FromString(TString Str)
|
||||
{
|
||||
Str.Remove('-');
|
||||
ASSERT(Str.Size() == 32);
|
||||
|
||||
CSavedStateID Out;
|
||||
Out.m[0] = Str.SubString(0, 16).ToInt64(16);
|
||||
Out.m[1] = Str.SubString(16, 16).ToInt64(16);
|
||||
return Out;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // CSAVEDSTATEID_H
|
|
@ -94,12 +94,25 @@ void CWorld::Serialize(IArchive& rArc)
|
|||
if (rArc.Game() >= eEchoesDemo && rArc.Game() <= eCorruption)
|
||||
rArc << SERIAL("TempleKeyWorldIndex", mTempleKeyWorldIndex);
|
||||
|
||||
if (rArc.Game() == eReturns)
|
||||
rArc << SERIAL("TimeAttackData", mTimeAttackData);
|
||||
|
||||
if (rArc.Game() == ePrime)
|
||||
rArc << SERIAL_CONTAINER("MemoryRelays", mMemoryRelays, "MemoryRelay");
|
||||
|
||||
rArc << SERIAL_CONTAINER("Areas", mAreas, "Area");
|
||||
}
|
||||
|
||||
void Serialize(IArchive& rArc, CWorld::STimeAttackData& rData)
|
||||
{
|
||||
rArc << SERIAL("HasTimeAttack", rData.HasTimeAttack)
|
||||
<< SERIAL("ActNumber", rData.ActNumber)
|
||||
<< SERIAL("BronzeTime", rData.BronzeTime)
|
||||
<< SERIAL("SilverTime", rData.SilverTime)
|
||||
<< SERIAL("GoldTime", rData.GoldTime)
|
||||
<< SERIAL("ShinyGoldTime", rData.ShinyGoldTime);
|
||||
}
|
||||
|
||||
void Serialize(IArchive& rArc, CWorld::SMemoryRelay& rMemRelay)
|
||||
{
|
||||
rArc << SERIAL_HEX("MemoryRelayID", rMemRelay.InstanceID)
|
||||
|
@ -140,6 +153,11 @@ void Serialize(IArchive& rArc, CWorld::SArea::SLayer& rLayer)
|
|||
{
|
||||
rArc << SERIAL("Name", rLayer.LayerName)
|
||||
<< SERIAL("Active", rLayer.Active);
|
||||
|
||||
if (rArc.Game() >= eCorruption)
|
||||
{
|
||||
rArc << SERIAL("StateID", rLayer.LayerStateID);
|
||||
}
|
||||
}
|
||||
|
||||
void Serialize(IArchive& rArc, CWorld::SAudioGrp& rAudioGrp)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define CWORLD_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CSavedStateID.h"
|
||||
#include "CStringTable.h"
|
||||
#include "Core/Resource/Area/CGameArea.h"
|
||||
#include "Core/Resource/Model/CModel.h"
|
||||
|
@ -22,6 +23,16 @@ class CWorld : public CResource
|
|||
TResPtr<CResource> mpMapWorld;
|
||||
u32 mTempleKeyWorldIndex;
|
||||
|
||||
struct STimeAttackData
|
||||
{
|
||||
bool HasTimeAttack;
|
||||
TString ActNumber;
|
||||
float BronzeTime;
|
||||
float SilverTime;
|
||||
float GoldTime;
|
||||
float ShinyGoldTime;
|
||||
} mTimeAttackData;
|
||||
|
||||
struct SAudioGrp
|
||||
{
|
||||
CAssetID ResID;
|
||||
|
@ -69,7 +80,7 @@ class CWorld : public CResource
|
|||
{
|
||||
TString LayerName;
|
||||
bool Active;
|
||||
u8 LayerID[16];
|
||||
CSavedStateID LayerStateID;
|
||||
};
|
||||
std::vector<SLayer> Layers;
|
||||
};
|
||||
|
@ -86,6 +97,7 @@ public:
|
|||
|
||||
// Serialization
|
||||
virtual void Serialize(IArchive& rArc);
|
||||
friend void Serialize(IArchive& rArc, STimeAttackData& rTimeAttackData);
|
||||
friend void Serialize(IArchive& rArc, SMemoryRelay& rMemRelay);
|
||||
friend void Serialize(IArchive& rArc, SArea& rArea);
|
||||
friend void Serialize(IArchive& rArc, SArea::SDock& rDock);
|
||||
|
|
|
@ -1,11 +1,26 @@
|
|||
#include "CAreaCooker.h"
|
||||
#include "CScriptCooker.h"
|
||||
#include "Core/CompressionUtil.h"
|
||||
#include "Core/GameProject/DependencyListBuilders.h"
|
||||
#include <Common/Log.h>
|
||||
|
||||
const bool gkForceDisableCompression = false;
|
||||
const bool gkForceDisableCompression = true;
|
||||
|
||||
CAreaCooker::CAreaCooker()
|
||||
: mGeometrySecNum(-1)
|
||||
, mSCLYSecNum(-1)
|
||||
, mSCGNSecNum(-1)
|
||||
, mCollisionSecNum(-1)
|
||||
, mUnknownSecNum(-1)
|
||||
, mLightsSecNum(-1)
|
||||
, mVISISecNum(-1)
|
||||
, mPATHSecNum(-1)
|
||||
, mAROTSecNum(-1)
|
||||
, mFFFFSecNum(-1)
|
||||
, mPTLASecNum(-1)
|
||||
, mEGMCSecNum(-1)
|
||||
, mDepsSecNum(-1)
|
||||
, mModulesSecNum(-1)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -69,8 +84,10 @@ void CAreaCooker::DetermineSectionNumbersCorruption()
|
|||
for (u32 iNum = 0; iNum < mpArea->mSectionNumbers.size(); iNum++)
|
||||
{
|
||||
CGameArea::SSectionNumber& rNum = mpArea->mSectionNumbers[iNum];
|
||||
if (rNum.SectionID == "SOBJ") mSCLYSecNum = rNum.Index;
|
||||
if (rNum.SectionID == "SOBJ") mSCLYSecNum = rNum.Index;
|
||||
else if (rNum.SectionID == "SGEN") mSCGNSecNum = rNum.Index;
|
||||
else if (rNum.SectionID == "DEPS") mDepsSecNum = rNum.Index;
|
||||
else if (rNum.SectionID == "RSOS") mModulesSecNum = rNum.Index;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,6 +245,57 @@ void CAreaCooker::WriteEchoesSCLY(IOutputStream& rOut)
|
|||
FinishSection(true);
|
||||
}
|
||||
|
||||
void CAreaCooker::WriteDependencies(IOutputStream& rOut)
|
||||
{
|
||||
// Build dependency list
|
||||
std::list<CAssetID> Dependencies;
|
||||
std::list<u32> LayerOffsets;
|
||||
|
||||
CAreaDependencyListBuilder Builder(mpArea->Entry());
|
||||
Builder.BuildDependencyList(Dependencies, LayerOffsets);
|
||||
|
||||
// Write
|
||||
rOut.WriteLong(Dependencies.size());
|
||||
|
||||
for (auto Iter = Dependencies.begin(); Iter != Dependencies.end(); Iter++)
|
||||
{
|
||||
CAssetID ID = *Iter;
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(ID);
|
||||
ID.Write(rOut);
|
||||
pEntry->CookedExtension().Write(rOut);
|
||||
}
|
||||
|
||||
rOut.WriteLong(LayerOffsets.size());
|
||||
|
||||
for (auto Iter = LayerOffsets.begin(); Iter != LayerOffsets.end(); Iter++)
|
||||
rOut.WriteLong(*Iter);
|
||||
|
||||
FinishSection(false);
|
||||
}
|
||||
|
||||
void CAreaCooker::WriteModules(IOutputStream& rOut)
|
||||
{
|
||||
// Build module list
|
||||
std::vector<TString> ModuleNames;
|
||||
std::vector<u32> LayerOffsets;
|
||||
|
||||
CAreaDependencyTree *pAreaDeps = static_cast<CAreaDependencyTree*>(mpArea->Entry()->Dependencies());
|
||||
pAreaDeps->GetModuleDependencies(mpArea->Game(), ModuleNames, LayerOffsets);
|
||||
|
||||
// Write
|
||||
rOut.WriteLong(ModuleNames.size());
|
||||
|
||||
for (u32 ModuleIdx = 0; ModuleIdx < ModuleNames.size(); ModuleIdx++)
|
||||
rOut.WriteString( ModuleNames[ModuleIdx] );
|
||||
|
||||
rOut.WriteLong(LayerOffsets.size());
|
||||
|
||||
for (u32 OffsetIdx = 0; OffsetIdx < LayerOffsets.size(); OffsetIdx++)
|
||||
rOut.WriteLong(LayerOffsets[OffsetIdx]);
|
||||
|
||||
FinishSection(false);
|
||||
}
|
||||
|
||||
// ************ SECTION MANAGEMENT ************
|
||||
void CAreaCooker::AddSectionToBlock()
|
||||
{
|
||||
|
@ -322,8 +390,14 @@ bool CAreaCooker::CookMREA(CGameArea *pArea, IOutputStream& rOut)
|
|||
// Write pre-SCLY data sections
|
||||
for (u32 iSec = 0; iSec < Cooker.mSCLYSecNum; iSec++)
|
||||
{
|
||||
Cooker.mSectionData.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size());
|
||||
Cooker.FinishSection(false);
|
||||
if (iSec == Cooker.mDepsSecNum)
|
||||
Cooker.WriteDependencies(Cooker.mSectionData);
|
||||
|
||||
else
|
||||
{
|
||||
Cooker.mSectionData.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size());
|
||||
Cooker.FinishSection(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Write SCLY
|
||||
|
@ -336,8 +410,14 @@ bool CAreaCooker::CookMREA(CGameArea *pArea, IOutputStream& rOut)
|
|||
u32 PostSCLY = (Cooker.mVersion <= ePrime ? Cooker.mSCLYSecNum + 1 : Cooker.mSCGNSecNum + 1);
|
||||
for (u32 iSec = PostSCLY; iSec < pArea->mSectionDataBuffers.size(); iSec++)
|
||||
{
|
||||
Cooker.mSectionData.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size());
|
||||
Cooker.FinishSection(false);
|
||||
if (iSec == Cooker.mModulesSecNum)
|
||||
Cooker.WriteModules(Cooker.mSectionData);
|
||||
|
||||
else
|
||||
{
|
||||
Cooker.mSectionData.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size());
|
||||
Cooker.FinishSection(false);
|
||||
}
|
||||
}
|
||||
|
||||
Cooker.FinishBlock();
|
||||
|
|
|
@ -25,6 +25,8 @@ class CAreaCooker
|
|||
u32 mFFFFSecNum;
|
||||
u32 mPTLASecNum;
|
||||
u32 mEGMCSecNum;
|
||||
u32 mDepsSecNum;
|
||||
u32 mModulesSecNum;
|
||||
|
||||
struct SCompressedBlock
|
||||
{
|
||||
|
@ -57,6 +59,10 @@ class CAreaCooker
|
|||
void WritePrimeSCLY(IOutputStream& rOut);
|
||||
void WriteEchoesSCLY(IOutputStream& rOut);
|
||||
|
||||
// Other Sections
|
||||
void WriteDependencies(IOutputStream& rOut);
|
||||
void WriteModules(IOutputStream& rOut);
|
||||
|
||||
// Section Management
|
||||
void AddSectionToBlock();
|
||||
void FinishSection(bool ForceFinishBlock);
|
||||
|
|
|
@ -25,7 +25,24 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
|
|||
if (Game == eEchoesDemo || Game == eEchoes)
|
||||
{
|
||||
DarkWorldNameID.Write(rMLVL);
|
||||
rMLVL.WriteLong(0);
|
||||
}
|
||||
if (Game >= eEchoesDemo && Game <= eCorruption)
|
||||
{
|
||||
rMLVL.WriteLong(pWorld->mTempleKeyWorldIndex);
|
||||
}
|
||||
if (Game == eReturns)
|
||||
{
|
||||
const CWorld::STimeAttackData& rkData = pWorld->mTimeAttackData;
|
||||
rMLVL.WriteBool(rkData.HasTimeAttack);
|
||||
|
||||
if (rkData.HasTimeAttack)
|
||||
{
|
||||
rMLVL.WriteString(rkData.ActNumber);
|
||||
rMLVL.WriteFloat(rkData.BronzeTime);
|
||||
rMLVL.WriteFloat(rkData.SilverTime);
|
||||
rMLVL.WriteFloat(rkData.GoldTime);
|
||||
rMLVL.WriteFloat(rkData.ShinyGoldTime);
|
||||
}
|
||||
}
|
||||
|
||||
SaveWorldID.Write(rMLVL);
|
||||
|
@ -142,6 +159,10 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
|
|||
rMLVL.WriteLong(ModuleLayerOffsets[iOff]);
|
||||
}
|
||||
|
||||
// Unknown
|
||||
if (Game == eReturns)
|
||||
rMLVL.WriteLong(0);
|
||||
|
||||
// Internal Name
|
||||
if (Game >= eEchoesDemo)
|
||||
rMLVL.WriteString(rArea.InternalName);
|
||||
|
@ -191,6 +212,7 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
|
|||
// Layers
|
||||
rMLVL.WriteLong(pWorld->mAreas.size());
|
||||
std::vector<TString> LayerNames;
|
||||
std::vector<CSavedStateID> LayerStateIDs;
|
||||
std::vector<u32> LayerNameOffsets;
|
||||
|
||||
// Layer Flags
|
||||
|
@ -209,6 +231,7 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
|
|||
LayerActiveFlags &= ~(1 << iLyr);
|
||||
|
||||
LayerNames.push_back(rLayer.LayerName);
|
||||
LayerStateIDs.push_back(rLayer.LayerStateID);
|
||||
}
|
||||
|
||||
rMLVL.WriteLongLong(LayerActiveFlags);
|
||||
|
@ -220,9 +243,13 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
|
|||
for (u32 iLyr = 0; iLyr < LayerNames.size(); iLyr++)
|
||||
rMLVL.WriteString(LayerNames[iLyr]);
|
||||
|
||||
// todo: Layer Saved State IDs go here for MP3/DKCR; need support for saved state IDs to implement
|
||||
if (Game == eCorruption || Game == eReturns)
|
||||
// Layer Saved State IDs
|
||||
if (Game >= eCorruption)
|
||||
{
|
||||
rMLVL.WriteLong(LayerStateIDs.size());
|
||||
|
||||
for (u32 iLyr = 0; iLyr < LayerStateIDs.size(); iLyr++)
|
||||
LayerStateIDs[iLyr].Write(rMLVL);
|
||||
}
|
||||
|
||||
// Layer Name Offsets
|
||||
|
|
|
@ -167,19 +167,37 @@ void CWorldLoader::LoadPrimeMLVL(IInputStream& rMLVL)
|
|||
pArea->Layers[iLayer].LayerName = rMLVL.ReadString();
|
||||
}
|
||||
|
||||
// Layer state IDs
|
||||
if (mVersion >= eCorruption)
|
||||
{
|
||||
rMLVL.Seek(0x4, SEEK_CUR); // Skipping redundant layer count
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
CWorld::SArea *pArea = &mpWorld->mAreas[iArea];
|
||||
u32 NumLayers = pArea->Layers.size();
|
||||
|
||||
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
|
||||
pArea->Layers[iLayer].LayerStateID = CSavedStateID(rMLVL);
|
||||
}
|
||||
}
|
||||
|
||||
// Last part of the file is layer name offsets, but we don't need it
|
||||
// todo: Layer ID support for MP3
|
||||
}
|
||||
|
||||
void CWorldLoader::LoadReturnsMLVL(IInputStream& rMLVL)
|
||||
{
|
||||
mpWorld->mpWorldName = gpResourceStore->LoadResource<CStringTable>(rMLVL.ReadLongLong());
|
||||
|
||||
bool Check = (rMLVL.ReadByte() != 0);
|
||||
if (Check)
|
||||
CWorld::STimeAttackData& rData = mpWorld->mTimeAttackData;
|
||||
rData.HasTimeAttack = rMLVL.ReadBool();
|
||||
|
||||
if (rData.HasTimeAttack)
|
||||
{
|
||||
rMLVL.ReadString();
|
||||
rMLVL.Seek(0x10, SEEK_CUR);
|
||||
rData.ActNumber = rMLVL.ReadString();
|
||||
rData.BronzeTime = rMLVL.ReadFloat();
|
||||
rData.SilverTime = rMLVL.ReadFloat();
|
||||
rData.GoldTime = rMLVL.ReadFloat();
|
||||
rData.ShinyGoldTime = rMLVL.ReadFloat();
|
||||
}
|
||||
|
||||
mpWorld->mpSaveWorld = gpResourceStore->LoadResource(rMLVL.ReadLongLong(), eSaveWorld);
|
||||
|
@ -229,8 +247,18 @@ void CWorldLoader::LoadReturnsMLVL(IInputStream& rMLVL)
|
|||
pArea->Layers[iLayer].LayerName = rMLVL.ReadString();
|
||||
}
|
||||
|
||||
// Layer state IDs
|
||||
rMLVL.Seek(0x4, SEEK_CUR); // Skipping redundant layer count
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
CWorld::SArea *pArea = &mpWorld->mAreas[iArea];
|
||||
u32 NumLayers = pArea->Layers.size();
|
||||
|
||||
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
|
||||
pArea->Layers[iLayer].LayerStateID = CSavedStateID(rMLVL);
|
||||
}
|
||||
|
||||
// Last part of the file is layer name offsets, but we don't need it
|
||||
// todo: Layer ID support
|
||||
}
|
||||
|
||||
void CWorldLoader::GenerateEditorData()
|
||||
|
|
Loading…
Reference in New Issue