mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-20 02:15:32 +00:00
Initial commit of current work on Prime World Editor
This commit is contained in:
538
Resource/factory/CAreaLoader.cpp
Normal file
538
Resource/factory/CAreaLoader.cpp
Normal file
@@ -0,0 +1,538 @@
|
||||
#include "CAreaLoader.h"
|
||||
#include "CCollisionLoader.h"
|
||||
#include "CModelLoader.h"
|
||||
#include "CMaterialLoader.h"
|
||||
#include "CScriptLoader.h"
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/CompressionUtil.h>
|
||||
#include <iostream>
|
||||
#include <Core/Log.h>
|
||||
|
||||
CAreaLoader::CAreaLoader()
|
||||
{
|
||||
mpMREA = nullptr;
|
||||
mHasDecompressedBuffer = false;
|
||||
mGeometryBlockNum = -1;
|
||||
mScriptLayerBlockNum = -1;
|
||||
mCollisionBlockNum = -1;
|
||||
mUnknownBlockNum = -1;
|
||||
mLightsBlockNum = -1;
|
||||
mEmptyBlockNum = -1;
|
||||
mPathBlockNum = -1;
|
||||
mOctreeBlockNum = -1;
|
||||
mScriptGeneratorBlockNum = -1;
|
||||
mFFFFBlockNum = -1;
|
||||
mUnknown2BlockNum = -1;
|
||||
mEGMCBlockNum = -1;
|
||||
mBoundingBoxesBlockNum = -1;
|
||||
mDependenciesBlockNum = -1;
|
||||
mGPUBlockNum = -1;
|
||||
mPVSBlockNum = -1;
|
||||
mRSOBlockNum = -1;
|
||||
}
|
||||
|
||||
CAreaLoader::~CAreaLoader()
|
||||
{
|
||||
if (mHasDecompressedBuffer)
|
||||
{
|
||||
delete mpMREA;
|
||||
delete[] mDecmpBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ PRIME ************
|
||||
void CAreaLoader::ReadHeaderPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP1)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
u32 mNumBlocks = mpMREA->ReadLong();
|
||||
|
||||
mGeometryBlockNum = mpMREA->ReadLong();
|
||||
mScriptLayerBlockNum = mpMREA->ReadLong();
|
||||
mCollisionBlockNum = mpMREA->ReadLong();
|
||||
mUnknownBlockNum = mpMREA->ReadLong();
|
||||
mLightsBlockNum = mpMREA->ReadLong();
|
||||
mEmptyBlockNum = mpMREA->ReadLong();
|
||||
mPathBlockNum = mpMREA->ReadLong();
|
||||
mOctreeBlockNum = mpMREA->ReadLong();
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(mNumBlocks, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadGeometryPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)");
|
||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mBlockMgr->ToNextBlock();
|
||||
|
||||
// Geometry
|
||||
for (u32 m = 0; m < mNumMeshes; m++) {
|
||||
std::cout << "\rLoading mesh " << std::dec << m + 1 << "/" << mNumMeshes;
|
||||
|
||||
SModelData *data = CModelLoader::LoadWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, mVersion);
|
||||
CModel *pTerrainModel = new CModel(data, mpArea->mMaterialSet);
|
||||
mpArea->AddWorldModel(pTerrainModel);
|
||||
|
||||
if (mVersion >= eEchoes) {
|
||||
mBlockMgr->ToNextBlock();
|
||||
mBlockMgr->ToNextBlock();
|
||||
}
|
||||
}
|
||||
mpArea->MergeTerrain();
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadSCLYPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)");
|
||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
||||
|
||||
CFourCC SCLY(*mpMREA);
|
||||
if (SCLY != "SCLY")
|
||||
{
|
||||
Log::Error(mpMREA->GetSourceString() + " - Invalid SCLY magic: " + SCLY.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
mpMREA->Seek(0x4, SEEK_CUR);
|
||||
mNumLayers = mpMREA->ReadLong();
|
||||
mpArea->mScriptLayers.reserve(mNumLayers);
|
||||
|
||||
std::vector<u32> LayerSizes(mNumLayers);
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
LayerSizes[l] = mpMREA->ReadLong();
|
||||
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
{
|
||||
u32 next = mpMREA->Tell() + LayerSizes[l];
|
||||
|
||||
CScriptLayer *layer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
if (layer)
|
||||
mpArea->mScriptLayers.push_back(layer);
|
||||
|
||||
mpMREA->Seek(next, SEEK_SET);
|
||||
}
|
||||
|
||||
SetUpObjects();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadLightsPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)");
|
||||
mBlockMgr->ToBlock(mLightsBlockNum);
|
||||
|
||||
u32 babedead = mpMREA->ReadLong();
|
||||
if (babedead != 0xbabedead) return;
|
||||
|
||||
mpArea->mLightLayers.resize(2);
|
||||
|
||||
for (u32 ly = 0; ly < 2; ly++)
|
||||
{
|
||||
u32 NumLights = mpMREA->ReadLong();
|
||||
mpArea->mLightLayers[ly].resize(NumLights);
|
||||
|
||||
for (u32 l = 0; l < NumLights; l++)
|
||||
{
|
||||
ELightType Type = ELightType(mpMREA->ReadLong());
|
||||
CVector3f Color(*mpMREA);
|
||||
CVector3f Position(*mpMREA);
|
||||
CVector3f Direction(*mpMREA);
|
||||
float Multiplier = mpMREA->ReadFloat();
|
||||
float SpotCutoff = mpMREA->ReadFloat();
|
||||
mpMREA->Seek(0x9, SEEK_CUR);
|
||||
u32 FalloffType = mpMREA->ReadLong();
|
||||
mpMREA->Seek(0x4, SEEK_CUR);
|
||||
|
||||
// Relevant data is read - now we process and form a CLight out of it
|
||||
CLight *Light;
|
||||
|
||||
CColor LightColor = CColor(Color.x, Color.y, Color.z, 0.f);
|
||||
if (Multiplier < FLT_EPSILON)
|
||||
Multiplier = FLT_EPSILON;
|
||||
|
||||
// Local Ambient
|
||||
if (Type == eLocalAmbient)
|
||||
{
|
||||
Color *= Multiplier;
|
||||
|
||||
// Clamp
|
||||
if (Color.x > 1.f) Color.x = 1.f;
|
||||
if (Color.y > 1.f) Color.y = 1.f;
|
||||
if (Color.z > 1.f) Color.z = 1.f;
|
||||
CColor MultColor(Color.x, Color.y, Color.z, 1.f);
|
||||
|
||||
Light = CLight::BuildLocalAmbient(Position, MultColor);
|
||||
}
|
||||
|
||||
// Directional
|
||||
else if (Type == eDirectional)
|
||||
{
|
||||
Light = CLight::BuildDirectional(Position, Direction, LightColor);
|
||||
}
|
||||
|
||||
// Spot
|
||||
else if (Type == eSpot)
|
||||
{
|
||||
Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff);
|
||||
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC);
|
||||
}
|
||||
|
||||
// Custom
|
||||
else
|
||||
{
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
|
||||
Light = CLight::BuildCustom(Position, Direction, LightColor,
|
||||
DistAttenA, DistAttenB, DistAttenC,
|
||||
1.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
mpArea->mLightLayers[ly][l] = Light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ ECHOES ************
|
||||
void CAreaLoader::ReadHeaderEchoes()
|
||||
{
|
||||
// This function reads the header for Echoes and the Echoes demo disc
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP2)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
if (mVersion == eEchoes) mNumLayers = mpMREA->ReadLong();
|
||||
u32 numBlocks = mpMREA->ReadLong();
|
||||
|
||||
mGeometryBlockNum = mpMREA->ReadLong();
|
||||
mScriptLayerBlockNum = mpMREA->ReadLong();
|
||||
mScriptGeneratorBlockNum = mpMREA->ReadLong();
|
||||
mCollisionBlockNum = mpMREA->ReadLong();
|
||||
mUnknownBlockNum = mpMREA->ReadLong();
|
||||
mLightsBlockNum = mpMREA->ReadLong();
|
||||
mEmptyBlockNum = mpMREA->ReadLong();
|
||||
mPathBlockNum = mpMREA->ReadLong();
|
||||
mFFFFBlockNum = mpMREA->ReadLong();
|
||||
mUnknown2BlockNum = mpMREA->ReadLong();
|
||||
mEGMCBlockNum = mpMREA->ReadLong();
|
||||
if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong());
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(numBlocks, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
ReadCompressedBlocks();
|
||||
Decompress();
|
||||
}
|
||||
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadSCLYEchoes()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)");
|
||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
||||
|
||||
// SCLY
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
{
|
||||
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
|
||||
if (pLayer)
|
||||
mpArea->mScriptLayers.push_back(pLayer);
|
||||
|
||||
mBlockMgr->ToNextBlock();
|
||||
}
|
||||
|
||||
// SCGN
|
||||
mBlockMgr->ToBlock(mScriptGeneratorBlockNum);
|
||||
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
|
||||
if (pLayer)
|
||||
mpArea->mpGeneratorLayer = pLayer;
|
||||
|
||||
SetUpObjects();
|
||||
}
|
||||
|
||||
// ************ CORRUPTION ************
|
||||
void CAreaLoader::ReadHeaderCorruption()
|
||||
{
|
||||
// This function reads the header for MP3, the MP3 prototype, and DKCR
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP3/DKCR)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
mNumLayers = mpMREA->ReadLong();
|
||||
u32 NumSections = mpMREA->ReadLong();
|
||||
mClusters.resize(mpMREA->ReadLong());
|
||||
u32 SectionNumberCount = mpMREA->ReadLong();
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(NumSections, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
ReadCompressedBlocks();
|
||||
|
||||
for (u32 iNum = 0; iNum < SectionNumberCount; iNum++)
|
||||
{
|
||||
CFourCC Type(*mpMREA);
|
||||
u32 Num = mpMREA->ReadLong();
|
||||
|
||||
if (Type == "AABB") mBoundingBoxesBlockNum = Num;
|
||||
else if (Type == "COLI") mCollisionBlockNum = Num;
|
||||
else if (Type == "DEPS") mDependenciesBlockNum = Num;
|
||||
else if (Type == "EGMC") mEGMCBlockNum = Num;
|
||||
else if (Type == "GPUD") mGPUBlockNum = Num;
|
||||
else if (Type == "LITE") mLightsBlockNum = Num;
|
||||
else if (Type == "PFL2") mPathBlockNum = Num;
|
||||
else if (Type == "PVS!") mPVSBlockNum = Num;
|
||||
else if (Type == "ROCT") mOctreeBlockNum = Num;
|
||||
else if (Type == "RSOS") mRSOBlockNum = Num;
|
||||
else if (Type == "SOBJ") mScriptLayerBlockNum = Num;
|
||||
else if (Type == "SGEN") mScriptGeneratorBlockNum = Num;
|
||||
else if (Type == "WOBJ") mGeometryBlockNum = Num; // note WOBJ can show up multiple times, but is always 0
|
||||
}
|
||||
|
||||
mpMREA->SeekToBoundary(32);
|
||||
Decompress();
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadGeometryCorruption()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)");
|
||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mBlockMgr->ToNextBlock();
|
||||
|
||||
// Geometry
|
||||
u32 CurWOBJSection = 1;
|
||||
u32 CurGPUSection = mGPUBlockNum;
|
||||
|
||||
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
|
||||
{
|
||||
std::cout << "\rLoading mesh " << std::dec << iMesh + 1 << "/" << mNumMeshes;
|
||||
|
||||
SModelData *pData = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
|
||||
CModel *pWorldModel = new CModel(pData, mpArea->mMaterialSet);
|
||||
mpArea->AddWorldModel(pWorldModel);
|
||||
|
||||
CurWOBJSection += 4;
|
||||
CurGPUSection = mBlockMgr->CurrentBlock();
|
||||
}
|
||||
|
||||
mpArea->MergeTerrain();
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
// ************ RETURNS ************
|
||||
|
||||
// ************ COMMON ************
|
||||
void CAreaLoader::ReadCompressedBlocks()
|
||||
{
|
||||
mTotalDecmpSize = 0;
|
||||
|
||||
for (u32 c = 0; c < mClusters.size(); c++)
|
||||
{
|
||||
mClusters[c].BufferSize = mpMREA->ReadLong();
|
||||
mClusters[c].DecompressedSize = mpMREA->ReadLong();
|
||||
mClusters[c].CompressedSize = mpMREA->ReadLong();
|
||||
mClusters[c].NumSections = mpMREA->ReadLong();
|
||||
mTotalDecmpSize += mClusters[c].DecompressedSize;
|
||||
}
|
||||
|
||||
mpMREA->SeekToBoundary(32);
|
||||
}
|
||||
|
||||
void CAreaLoader::Decompress()
|
||||
{
|
||||
// This function decompresses compressed clusters into a buffer.
|
||||
// It should be called at the beginning of the first compressed cluster.
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Decompressing MREA data");
|
||||
if (mVersion < eEchoes) return;
|
||||
|
||||
// Decompress clusters
|
||||
mDecmpBuffer = new u8[mTotalDecmpSize];
|
||||
u32 Offset = 0;
|
||||
|
||||
for (u32 c = 0; c < mClusters.size(); c++)
|
||||
{
|
||||
SCompressedCluster *cc = &mClusters[c];
|
||||
|
||||
// Is it decompressed already?
|
||||
if (mClusters[c].CompressedSize == 0)
|
||||
{
|
||||
mpMREA->ReadBytes(mDecmpBuffer + Offset, cc->DecompressedSize);
|
||||
Offset += cc->DecompressedSize;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
u32 StartOffset = 32 - (mClusters[c].CompressedSize % 32); // For some reason they pad the beginning instead of the end
|
||||
if (StartOffset != 32)
|
||||
mpMREA->Seek(StartOffset, SEEK_CUR);
|
||||
|
||||
std::vector<u8> cmp(mClusters[c].CompressedSize);
|
||||
mpMREA->ReadBytes(cmp.data(), cmp.size());
|
||||
|
||||
bool Success = CompressionUtil::DecompressAreaLZO(cmp.data(), cmp.size(), mDecmpBuffer + Offset, cc->DecompressedSize);
|
||||
if (!Success)
|
||||
throw "Failed to decompress MREA!";
|
||||
|
||||
Offset += cc->DecompressedSize;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Source = mpMREA->GetSourceString();
|
||||
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::BigEndian);
|
||||
mpMREA->SetSourceString(Source);
|
||||
mBlockMgr->SetInputStream(mpMREA);
|
||||
mHasDecompressedBuffer = true;
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadCollision()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
|
||||
mBlockMgr->ToBlock(mCollisionBlockNum);
|
||||
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
|
||||
}
|
||||
|
||||
void CAreaLoader::SetUpObjects()
|
||||
{
|
||||
// Iterate over all objects
|
||||
for (u32 iLyr = 0; iLyr < mpArea->GetScriptLayerCount() + 1; iLyr++)
|
||||
{
|
||||
CScriptLayer *pLayer;
|
||||
if (iLyr < mpArea->GetScriptLayerCount()) pLayer = mpArea->mScriptLayers[iLyr];
|
||||
|
||||
else
|
||||
{
|
||||
pLayer = mpArea->GetGeneratorLayer();
|
||||
if (!pLayer) break;
|
||||
}
|
||||
|
||||
for (u32 iObj = 0; iObj < pLayer->GetNumObjects(); iObj++)
|
||||
{
|
||||
// Add object to object map
|
||||
CScriptObject *pObj = (*pLayer)[iObj];
|
||||
mpArea->mObjectMap[pObj->InstanceID()] = pObj;
|
||||
|
||||
// Store outgoing connections
|
||||
for (u32 iCon = 0; iCon < pObj->NumOutLinks(); iCon++)
|
||||
{
|
||||
SLink Connection = pObj->OutLink(iCon);
|
||||
|
||||
SLink NewConnection;
|
||||
NewConnection.State = Connection.State;
|
||||
NewConnection.Message = Connection.Message;
|
||||
NewConnection.ObjectID = pObj->InstanceID();
|
||||
mConnectionMap[Connection.ObjectID].push_back(NewConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store connections
|
||||
for (auto it = mpArea->mObjectMap.begin(); it != mpArea->mObjectMap.end(); it++)
|
||||
{
|
||||
u32 InstanceID = it->first;
|
||||
auto iConMap = mConnectionMap.find(InstanceID);
|
||||
|
||||
if (iConMap != mConnectionMap.end())
|
||||
{
|
||||
CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID);
|
||||
pObj->mInConnections = iConMap->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CGameArea* CAreaLoader::LoadMREA(CInputStream& MREA)
|
||||
{
|
||||
CAreaLoader Loader;
|
||||
|
||||
// Validation
|
||||
if (!MREA.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + MREA.GetSourceString());
|
||||
|
||||
u32 deadbeef = MREA.ReadLong();
|
||||
if (deadbeef != 0xdeadbeef)
|
||||
{
|
||||
Log::FileError(MREA.GetSourceString(), "Invalid MREA magic: " + StringUtil::ToHexString(deadbeef));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Header
|
||||
Loader.mpArea = new CGameArea;
|
||||
u32 version = MREA.ReadLong();
|
||||
Loader.mVersion = GetFormatVersion(version);
|
||||
Loader.mpMREA = &MREA;
|
||||
|
||||
switch (Loader.mVersion)
|
||||
{
|
||||
case ePrimeKioskDemo:
|
||||
case ePrime:
|
||||
Loader.ReadHeaderPrime();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYPrime();
|
||||
Loader.ReadCollision();
|
||||
Loader.ReadLightsPrime();
|
||||
break;
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Loader.ReadHeaderEchoes();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
Loader.ReadLightsPrime();
|
||||
break;
|
||||
case eCorruptionProto:
|
||||
Loader.ReadHeaderCorruption();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
break;
|
||||
case eCorruption:
|
||||
case eReturns:
|
||||
Loader.ReadHeaderCorruption();
|
||||
Loader.ReadGeometryCorruption();
|
||||
Loader.ReadSCLYEchoes();
|
||||
if (Loader.mVersion != eReturns) Loader.ReadCollision();
|
||||
break;
|
||||
default:
|
||||
Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + StringUtil::ToHexString(version));
|
||||
delete Loader.mpArea;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
delete Loader.mBlockMgr;
|
||||
return Loader.mpArea;
|
||||
}
|
||||
|
||||
EGame CAreaLoader::GetFormatVersion(u32 version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 0xC: return ePrimeKioskDemo;
|
||||
case 0xF: return ePrime;
|
||||
case 0x15: return eEchoesDemo;
|
||||
case 0x19: return eEchoes;
|
||||
case 0x1D: return eCorruptionProto;
|
||||
case 0x1E: return eCorruption;
|
||||
case 0x20: return eReturns;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user