PrimeWorldEditor/Resource/factory/CScanLoader.cpp

183 lines
5.4 KiB
C++

#include "CScanLoader.h"
#include <Core/CResCache.h>
#include <Core/Log.h>
CScanLoader::CScanLoader()
{
}
CScan* CScanLoader::LoadScanMP1(CInputStream &SCAN)
{
// Basic support at the moment - don't read animation/scan image data
SCAN.Seek(0x4, SEEK_CUR); // Skip FRME ID
mpScan->mpStringTable = (CStringTable*) gResCache.GetResource(SCAN.ReadLong(), "STRG");
mpScan->mStringToken = CToken(mpScan->mpStringTable);
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
mpScan->mCategory = (CScan::ELogbookCategory) SCAN.ReadLong();
mpScan->mIsImportant = (SCAN.ReadByte() == 1);
mpScan->mVersion = ePrime;
return mpScan;
}
CScan* CScanLoader::LoadScanMP2(CInputStream& SCAN)
{
// The SCAN format in MP2 embeds a SNFO object using the same format as SCLY
// However since the contents of the file are consistent there's no need to delegate to CScriptLoader
SCAN.Seek(0x1, SEEK_CUR);
u32 NumInstances = SCAN.ReadLong();
if (NumInstances != 1) {
Log::FileError(SCAN.GetSourceString(), "SCAN has multiple instances");
return nullptr;
}
u32 ScanInfoStart = SCAN.Tell();
CFourCC SNFO(SCAN);
if (SNFO != "SNFO") {
Log::FileError(SCAN.GetSourceString(), ScanInfoStart, "Unrecognized SCAN object type: " + SNFO.ToString());
return nullptr;
}
SCAN.Seek(0x6, SEEK_CUR);
u16 NumConnections = SCAN.ReadShort();
if (NumConnections > 0) {
Log::FileWarning(SCAN.GetSourceString(), ScanInfoStart, "SNFO object in SCAN has connections");
SCAN.Seek(NumConnections * 0xC, SEEK_CUR);
}
u32 BasePropID = SCAN.ReadLong();
if (BasePropID != 0xFFFFFFFF) {
Log::FileError(SCAN.GetSourceString(), SCAN.Tell() - 4, "Invalid base proprty ID: " + StringUtil::ToHexString(BasePropID));
return nullptr;
}
mpScan = new CScan();
SCAN.Seek(0x2, SEEK_CUR);
u16 NumProperties = SCAN.ReadShort();
switch (NumProperties)
{
case 0x14:
LoadParamsMP2(SCAN);
break;
case 0x16:
LoadParamsMP3(SCAN);
break;
default:
Log::FileError(SCAN.GetSourceString(), SCAN.Tell() - 2, "Invalid SNFO property count: " + StringUtil::ToHexString(NumProperties));
delete mpScan;
return nullptr;
}
return mpScan;
}
void CScanLoader::LoadParamsMP2(CInputStream& SCAN)
{
// Function begins after the SNFO property count
for (u32 iProp = 0; iProp < 20; iProp++)
{
u32 PropertyID = SCAN.ReadLong();
u16 PropertySize = SCAN.ReadShort();
u32 Next = SCAN.Tell() + PropertySize;
switch (PropertyID)
{
case 0x2F5B6423:
mpScan->mpStringTable = (CStringTable*) gResCache.GetResource(SCAN.ReadLong(), "STRG");
mpScan->mStringToken = CToken(mpScan->mpStringTable);
break;
case 0xC308A322:
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
break;
case 0x7B714814:
mpScan->mIsImportant = (SCAN.ReadByte() != 0);
break;
}
SCAN.Seek(Next, SEEK_SET);
}
mpScan->mCategory = CScan::eNone;
mpScan->mVersion = eEchoes;
}
void CScanLoader::LoadParamsMP3(CInputStream& SCAN)
{
// Function begins after the SNFO property count
// Function is near-identical to the MP2 one, but when I add support
// for the other params, there will be more differences
for (u32 iProp = 0; iProp < 20; iProp++)
{
u32 PropertyID = SCAN.ReadLong();
u16 PropertySize = SCAN.ReadShort();
u32 Next = SCAN.Tell() + PropertySize;
switch (PropertyID)
{
case 0x2F5B6423:
mpScan->mpStringTable = (CStringTable*) gResCache.GetResource(SCAN.ReadLongLong(), "STRG");
mpScan->mStringToken = CToken(mpScan->mpStringTable);
break;
case 0xC308A322:
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
break;
case 0x7B714814:
mpScan->mIsImportant = (SCAN.ReadByte() != 0);
break;
}
SCAN.Seek(Next, SEEK_SET);
}
mpScan->mCategory = CScan::eNone;
mpScan->mVersion = eCorruption;
}
// ************ STATIC/PUBLIC ************
CScan* CScanLoader::LoadSCAN(CInputStream &SCAN)
{
if (!SCAN.IsValid()) return nullptr;
Log::Write("Loading " + SCAN.GetSourceString());
/* Switching to EGame enum here isn't really useful unfortunately
* because the MP1 demo can be 1, 2, or 3, while MP1 is 5 and MP2+ is 2
* MP1 is the only one that starts with 5 so that is a consistent check for now
* Better version checks will be implemented when the other versions are
* better-understood. */
u32 fileVersion = SCAN.ReadLong();
u32 magic = SCAN.ReadLong();
// Echoes+
if (CFourCC(fileVersion) == "SCAN")
{
// The MP2 load function will check for MP3
CScanLoader loader;
loader.mVersion = eEchoes;
return loader.LoadScanMP2(SCAN);
}
if (magic != 0x0BADBEEF)
{
Log::FileError(SCAN.GetSourceString(), "Invalid SCAN magic: " + StringUtil::ToHexString(magic));
return nullptr;
}
if (fileVersion != 5)
{
Log::FileError(SCAN.GetSourceString(), "Unsupported SCAN version: " + StringUtil::ToHexString(fileVersion));
return nullptr;
}
// MP1 SCAN - read the file!
CScanLoader loader;
loader.mVersion = ePrime;
loader.mpScan = new CScan();
return loader.LoadScanMP1(SCAN);
}