mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-11-09 09:51:58 +00:00
feat: QOS (007: Quantum of Solace) support (#894)
* feat: QOS (007: Quantum of Solace) support * feat: add qos localize loader * feat: add qos stringtable loader * chore: comment about official zones * fix: add qos gfximage runtime fields * chore: remove useless dev time comments * fix: remove all `static_asserts`s from asset headers causing build fail on x64 * chore: change union ordering of qos `entryInternalData` * chore: move qos asset type enum to qos.h * fix: qos and t4 xsurface index buffer * chore: implement qos MaterialInfo struct * chore: adjust some gfxworld struct members * chore: implement qos PhysPreset and PhysConstraints * chore: fix and adjust slight inconsistencies with qos zone code * chore: reorder qos ZoneCode.lua entries in order of asset type enum * chore: remove asset types that are not actual asset types in zones * chore: check common asset arrays with static_assert * chore: rename qos assets * chore: reorder qos in templates to be in alphabetical order * chore: use function for CopyMipLevel in Dx9TextureLoader * chore: use templates for qos MapEntsDumper * chore: add missing braces in XModelToCommonConverter * chore: add generic way to peek at zone data * Qos does not use a classic ZoneHeader so code needs to be more generic * fix: do not use raw_byte and raw_uint typedefs for qos * fix: not being able to write clipmapmp * chore: additional research on qos vertex property and stuff --------- Co-authored-by: Jan Laupetin <[email protected]>
This commit is contained in:
1 parent
5c0a7257fd
commit
9f49e0e31c
142 files changed
+6000
-97
No files matched your search
@@ -124,8 +124,9 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(ZoneDataPeeking& filePeek) const
|
||||
{
|
||||
const auto& header = filePeek.PeekStruct<ZoneHeader>();
|
||||
if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC)
|
||||
{
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
@@ -172,11 +173,11 @@ std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(c
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
const auto inspectResult = InspectZoneHeader(filePeek);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
@@ -190,6 +191,9 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
|
||||
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// Skip the initial header that we peeked at before
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(sizeof(ZoneHeader)));
|
||||
|
||||
// Add steps for loading the auth header which also contain the signature of the zone if it is signed.
|
||||
AddAuthHeaderSteps(*inspectResult, *zoneLoader, fileName);
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace IW3
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace IW4;
|
||||
@@ -44,8 +43,9 @@ namespace
|
||||
bool m_is_iw4x;
|
||||
};
|
||||
|
||||
std::optional<ZoneLoaderInspectionResultIW4> InspectZoneHeaderIw4(const ZoneHeader& header)
|
||||
std::optional<ZoneLoaderInspectionResultIW4> InspectZoneHeaderIw4(ZoneDataPeeking& filePeek)
|
||||
{
|
||||
const auto& header = filePeek.PeekStruct<ZoneHeader>();
|
||||
if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC)
|
||||
{
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_IW4X, std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)))
|
||||
@@ -248,20 +248,20 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(ZoneDataPeeking& filePeek) const
|
||||
{
|
||||
auto resultIw4 = InspectZoneHeaderIw4(header);
|
||||
auto resultIw4 = InspectZoneHeaderIw4(filePeek);
|
||||
if (!resultIw4)
|
||||
return std::nullopt;
|
||||
|
||||
return resultIw4->m_generic_result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeaderIw4(header);
|
||||
const auto inspectResult = InspectZoneHeaderIw4(filePeek);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
@@ -275,6 +275,9 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
|
||||
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// Skip the initial header that we peeked at before
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(sizeof(ZoneHeader)));
|
||||
|
||||
// Skip unknown 1 byte field that the game ignores as well
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(1));
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace IW4
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
|
||||
@@ -121,8 +121,9 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(ZoneDataPeeking& filePeek) const
|
||||
{
|
||||
const auto& header = filePeek.PeekStruct<ZoneHeader>();
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
return std::nullopt;
|
||||
|
||||
@@ -155,11 +156,11 @@ std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(c
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
const auto inspectResult = InspectZoneHeader(filePeek);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
@@ -173,6 +174,9 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
|
||||
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// Skip the initial header that we peeked at before
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(sizeof(ZoneHeader)));
|
||||
|
||||
// Skip unknown 1 byte field that the game ignores as well
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(1));
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace IW5
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
#include "ContentLoaderQOS.h"
|
||||
|
||||
#include "Game/QOS/AssetLoaderQOS.h"
|
||||
#include "Loading/Exception/UnsupportedAssetTypeException.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace QOS;
|
||||
|
||||
ContentLoader::ContentLoader(Zone& zone, ZoneInputStream& stream)
|
||||
: ContentLoaderBase(zone, stream),
|
||||
varXAssetList(nullptr),
|
||||
varXAsset(nullptr),
|
||||
varScriptStringList(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void ContentLoader::LoadScriptStringList(const bool atStreamStart)
|
||||
{
|
||||
assert(!atStreamStart);
|
||||
|
||||
if (varScriptStringList->strings != nullptr)
|
||||
{
|
||||
assert(GetZonePointerType(varScriptStringList->strings) == ZonePointerType::FOLLOWING);
|
||||
|
||||
#ifdef ARCH_x86
|
||||
varScriptStringList->strings = m_stream.Alloc<const char*>(4);
|
||||
#else
|
||||
varScriptStringList->strings = m_stream.AllocOutOfBlock<const char*>(4, varScriptStringList->count);
|
||||
#endif
|
||||
varXString = varScriptStringList->strings;
|
||||
LoadXStringArray(true, varScriptStringList->count);
|
||||
|
||||
if (varScriptStringList->strings && varScriptStringList->count > 0)
|
||||
m_zone.m_script_strings.InitializeForExistingZone(varScriptStringList->strings, static_cast<size_t>(varScriptStringList->count));
|
||||
}
|
||||
|
||||
assert(m_zone.m_script_strings.Count() <= SCR_STRING_MAX + 1);
|
||||
}
|
||||
|
||||
void ContentLoader::LoadXAsset(const bool atStreamStart) const
|
||||
{
|
||||
#define LOAD_ASSET(type_index, typeName, headerEntry) \
|
||||
case type_index: \
|
||||
{ \
|
||||
Loader_##typeName loader(m_zone, m_stream); \
|
||||
loader.Load(&varXAsset->header.headerEntry); \
|
||||
break; \
|
||||
}
|
||||
|
||||
assert(varXAsset != nullptr);
|
||||
|
||||
if (atStreamStart)
|
||||
m_stream.Load<XAsset>(varXAsset);
|
||||
|
||||
switch (varXAsset->type)
|
||||
{
|
||||
LOAD_ASSET(ASSET_TYPE_PHYSPRESET, PhysPreset, physPreset)
|
||||
LOAD_ASSET(ASSET_TYPE_PHYSCONSTRAINTS, PhysConstraints, physConstraints)
|
||||
LOAD_ASSET(ASSET_TYPE_DESTRUCTIBLEDEF, DestructibleDef, destructibleDef)
|
||||
LOAD_ASSET(ASSET_TYPE_XANIMPARTS, XAnimParts, parts)
|
||||
LOAD_ASSET(ASSET_TYPE_XMODEL, XModel, model)
|
||||
LOAD_ASSET(ASSET_TYPE_MATERIAL, Material, material)
|
||||
LOAD_ASSET(ASSET_TYPE_TECHNIQUE_SET, MaterialTechniqueSet, techniqueSet)
|
||||
LOAD_ASSET(ASSET_TYPE_IMAGE, GfxImage, image)
|
||||
LOAD_ASSET(ASSET_TYPE_SOUND, snd_alias_list_t, sound)
|
||||
LOAD_ASSET(ASSET_TYPE_SND_CURVE, SndCurve, sndCurve)
|
||||
LOAD_ASSET(ASSET_TYPE_CLIPMAP_SP, clipMap_t, clipMap)
|
||||
LOAD_ASSET(ASSET_TYPE_CLIPMAP_MP, clipMap_t, clipMap)
|
||||
LOAD_ASSET(ASSET_TYPE_COMWORLD, ComWorld, comWorld)
|
||||
LOAD_ASSET(ASSET_TYPE_GAMEWORLD_SP, GameWorldSp, gameWorldSp)
|
||||
LOAD_ASSET(ASSET_TYPE_GAMEWORLD_MP, GameWorldMp, gameWorldMp)
|
||||
LOAD_ASSET(ASSET_TYPE_MAP_ENTS, MapEnts, mapEnts)
|
||||
LOAD_ASSET(ASSET_TYPE_GFXWORLD, GfxWorld, gfxWorld)
|
||||
LOAD_ASSET(ASSET_TYPE_LIGHT_DEF, GfxLightDef, lightDef)
|
||||
LOAD_ASSET(ASSET_TYPE_FONT, Font_s, font)
|
||||
LOAD_ASSET(ASSET_TYPE_MENUFILE, MenuList, menuList)
|
||||
LOAD_ASSET(ASSET_TYPE_MENU, menuDef_t, menu)
|
||||
LOAD_ASSET(ASSET_TYPE_LOCALIZE_ENTRY, LocalizeEntry, localize)
|
||||
LOAD_ASSET(ASSET_TYPE_WEAPON, WeaponDef, weapon)
|
||||
LOAD_ASSET(ASSET_TYPE_FX, FxEffectDef, fx)
|
||||
LOAD_ASSET(ASSET_TYPE_IMPACT_FX, FxImpactTable, impactFx)
|
||||
LOAD_ASSET(ASSET_TYPE_RAWFILE, RawFile, rawfile)
|
||||
LOAD_ASSET(ASSET_TYPE_STRINGTABLE, StringTable, stringTable)
|
||||
LOAD_ASSET(ASSET_TYPE_XMLTREE, XmlTree, xmlTree)
|
||||
LOAD_ASSET(ASSET_TYPE_SCENE_ANIMATION, SceneAnimation, sceneAnimation)
|
||||
LOAD_ASSET(ASSET_TYPE_CUTSCENE, Cutscene, cutscene)
|
||||
LOAD_ASSET(ASSET_TYPE_CUSTOM_CAMERA, CustomCamera, customCamera)
|
||||
|
||||
default:
|
||||
{
|
||||
throw UnsupportedAssetTypeException(varXAsset->type);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOAD_ASSET
|
||||
}
|
||||
|
||||
void ContentLoader::LoadXAssetArray(const bool atStreamStart, const size_t count)
|
||||
{
|
||||
assert(varXAsset != nullptr);
|
||||
|
||||
if (atStreamStart)
|
||||
{
|
||||
#ifdef ARCH_x86
|
||||
m_stream.Load<XAsset>(varXAsset, count);
|
||||
#else
|
||||
const auto fill = m_stream.LoadWithFill(8u * count);
|
||||
|
||||
for (size_t index = 0; index < count; index++)
|
||||
{
|
||||
fill.Fill(varXAsset[index].type, 8u * index);
|
||||
fill.FillPtr(varXAsset[index].header.data, 8u * index + 4u);
|
||||
m_stream.AddPointerLookup(&varXAsset[index].header.data, fill.BlockBuffer(8u * index + 4u));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < count; index++)
|
||||
{
|
||||
LoadXAsset(false);
|
||||
varXAsset++;
|
||||
|
||||
#ifdef DEBUG_OFFSETS
|
||||
m_stream.DebugOffsets(index);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::Load()
|
||||
{
|
||||
XAssetList assetList{};
|
||||
varXAssetList = &assetList;
|
||||
|
||||
#ifdef ARCH_x86
|
||||
m_stream.LoadDataRaw(&assetList, sizeof(assetList));
|
||||
#else
|
||||
const auto fillAccessor = m_stream.LoadWithFill(16u);
|
||||
varScriptStringList = &varXAssetList->stringList;
|
||||
fillAccessor.Fill(varScriptStringList->count, 0u);
|
||||
fillAccessor.FillPtr(varScriptStringList->strings, 4u);
|
||||
|
||||
fillAccessor.Fill(varXAssetList->assetCount, 8u);
|
||||
fillAccessor.FillPtr(varXAssetList->assets, 12u);
|
||||
#endif
|
||||
|
||||
m_stream.PushBlock(XFILE_BLOCK_VIRTUAL);
|
||||
|
||||
varScriptStringList = &assetList.stringList;
|
||||
LoadScriptStringList(false);
|
||||
|
||||
if (assetList.assets != nullptr)
|
||||
{
|
||||
assert(GetZonePointerType(assetList.assets) == ZonePointerType::FOLLOWING);
|
||||
|
||||
#ifdef ARCH_x86
|
||||
assetList.assets = m_stream.Alloc<XAsset>(4);
|
||||
#else
|
||||
assetList.assets = m_stream.AllocOutOfBlock<XAsset>(4, assetList.assetCount);
|
||||
#endif
|
||||
varXAsset = assetList.assets;
|
||||
LoadXAssetArray(true, assetList.assetCount);
|
||||
}
|
||||
|
||||
m_stream.PopBlock();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/QOS/QOS.h"
|
||||
#include "Loading/ContentLoaderBase.h"
|
||||
#include "Loading/IContentLoadingEntryPoint.h"
|
||||
|
||||
namespace QOS
|
||||
{
|
||||
class ContentLoader final : public ContentLoaderBase, public IContentLoadingEntryPoint
|
||||
{
|
||||
public:
|
||||
ContentLoader(Zone& zone, ZoneInputStream& stream);
|
||||
void Load() override;
|
||||
|
||||
private:
|
||||
void LoadScriptStringList(bool atStreamStart);
|
||||
|
||||
void LoadXAsset(bool atStreamStart) const;
|
||||
void LoadXAssetArray(bool atStreamStart, size_t count);
|
||||
|
||||
XAssetList* varXAssetList;
|
||||
XAsset* varXAsset;
|
||||
ScriptStringList* varScriptStringList;
|
||||
};
|
||||
} // namespace QOS
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "gfximage_actions.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace QOS;
|
||||
|
||||
Actions_GfxImage::Actions_GfxImage(Zone& zone)
|
||||
: AssetLoadingActions(zone)
|
||||
{
|
||||
}
|
||||
|
||||
void Actions_GfxImage::OnImageLoaded(GfxImage* image) const
|
||||
{
|
||||
image->cardMemory.platform[0] = 0;
|
||||
}
|
||||
|
||||
void Actions_GfxImage::LoadImageData(GfxImageLoadDef* loadDef, GfxImage* image) const
|
||||
{
|
||||
const size_t loadDefSize = offsetof(GfxImageLoadDef, data) + loadDef->resourceSize;
|
||||
|
||||
image->texture.loadDef = static_cast<GfxImageLoadDef*>(m_zone.Memory().AllocRaw(loadDefSize));
|
||||
memcpy(image->texture.loadDef, loadDef, loadDefSize);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/QOS/QOS.h"
|
||||
#include "Loading/AssetLoadingActions.h"
|
||||
|
||||
namespace QOS
|
||||
{
|
||||
class Actions_GfxImage final : public AssetLoadingActions
|
||||
{
|
||||
public:
|
||||
explicit Actions_GfxImage(Zone& zone);
|
||||
|
||||
void OnImageLoaded(GfxImage* image) const;
|
||||
void LoadImageData(GfxImageLoadDef* loadDef, GfxImage* image) const;
|
||||
};
|
||||
} // namespace QOS
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "ZoneLoaderFactoryQOS.h"
|
||||
|
||||
#include "ContentLoaderQOS.h"
|
||||
#include "Game/GameLanguage.h"
|
||||
#include "Game/QOS/QOS.h"
|
||||
#include "Game/QOS/ZoneConstantsQOS.h"
|
||||
#include "Loading/Processor/ProcessorInflate.h"
|
||||
#include "Loading/Steps/StepAddProcessor.h"
|
||||
#include "Loading/Steps/StepAllocXBlocks.h"
|
||||
#include "Loading/Steps/StepLoadZoneContent.h"
|
||||
#include "Loading/Steps/StepSkipBytes.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/Endianness.h"
|
||||
|
||||
using namespace QOS;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr size_t REMAINING_FILE_HEADER_SIZE = ZoneConstants::FILE_HEADER_SIZE - sizeof(ZoneHeader);
|
||||
|
||||
static_assert(REMAINING_FILE_HEADER_SIZE == sizeof(uint32_t) * 4u);
|
||||
|
||||
void SetupBlock(ZoneLoader& zoneLoader)
|
||||
{
|
||||
#define XBLOCK_DEF(name, type) std::make_unique<XBlock>(STR(name), name, type)
|
||||
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(QOS::XFILE_BLOCK_TEMP, XBlockType::BLOCK_TYPE_TEMP));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(QOS::XFILE_BLOCK_RUNTIME, XBlockType::BLOCK_TYPE_RUNTIME));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(QOS::XFILE_BLOCK_VIRTUAL, XBlockType::BLOCK_TYPE_NORMAL));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(QOS::XFILE_BLOCK_LARGE, XBlockType::BLOCK_TYPE_NORMAL));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(QOS::XFILE_BLOCK_PHYSICAL, XBlockType::BLOCK_TYPE_NORMAL));
|
||||
|
||||
#undef XBLOCK_DEF
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(ZoneDataPeeking& filePeek) const
|
||||
{
|
||||
const auto& header = filePeek.PeekStruct<ZoneHeaderQos>();
|
||||
if (endianness::FromLittleEndian(header.version) == ZoneConstants::ZONE_VERSION_PC)
|
||||
{
|
||||
return ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::QOS,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
// There is no way to know whether unsigned zones are official.
|
||||
.m_is_official = false,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = false,
|
||||
};
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(filePeek);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
auto zone = std::make_unique<Zone>(fileName, 0, GameId::QOS, inspectResult->m_platform);
|
||||
auto* zonePtr = zone.get();
|
||||
zone->m_language = GameLanguage::LANGUAGE_NONE;
|
||||
|
||||
auto zoneLoader = std::make_unique<ZoneLoader>(std::move(zone));
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// Skip the initial header that we peeked at before
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(sizeof(ZoneHeaderQos)));
|
||||
|
||||
// Maybe external data size?
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(4));
|
||||
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAllocXBlocks());
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
|
||||
zoneLoader->AddLoadingStep(step::CreateStepLoadZoneContent(
|
||||
[zonePtr](ZoneInputStream& stream)
|
||||
{
|
||||
return std::make_unique<ContentLoader>(*zonePtr, stream);
|
||||
},
|
||||
32u,
|
||||
ZoneConstants::OFFSET_BLOCK_BIT_COUNT,
|
||||
ZoneConstants::INSERT_BLOCK,
|
||||
zonePtr->Memory(),
|
||||
std::move(progressCallback)));
|
||||
|
||||
return zoneLoader;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "Loading/IZoneLoaderFactory.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace QOS
|
||||
{
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
} // namespace QOS
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Loading/Steps/StepAllocXBlocks.h"
|
||||
#include "Loading/Steps/StepLoadZoneContent.h"
|
||||
#include "Loading/Steps/StepLoadZoneSizes.h"
|
||||
#include "Loading/Steps/StepSkipBytes.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/Endianness.h"
|
||||
|
||||
@@ -34,8 +35,9 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(ZoneDataPeeking& filePeek) const
|
||||
{
|
||||
const auto& header = filePeek.PeekStruct<ZoneHeader>();
|
||||
if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC
|
||||
&& !memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
@@ -54,11 +56,11 @@ std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(c
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
const auto inspectResult = InspectZoneHeader(filePeek);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
@@ -71,6 +73,10 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
|
||||
auto zoneLoader = std::make_unique<ZoneLoader>(std::move(zone));
|
||||
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// Skip the initial header that we peeked at before
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(sizeof(ZoneHeader)));
|
||||
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
|
||||
zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes());
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAllocXBlocks());
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace T4
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
#include "Loading/Steps/StepAllocXBlocks.h"
|
||||
#include "Loading/Steps/StepLoadZoneContent.h"
|
||||
#include "Loading/Steps/StepLoadZoneSizes.h"
|
||||
#include "Loading/Steps/StepSkipBytes.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -36,8 +36,9 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(ZoneDataPeeking& filePeek) const
|
||||
{
|
||||
const auto& header = filePeek.PeekStruct<ZoneHeader>();
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
return std::nullopt;
|
||||
|
||||
@@ -58,11 +59,11 @@ std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(c
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
const auto inspectResult = InspectZoneHeader(filePeek);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
@@ -76,6 +77,9 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
|
||||
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// Skip the initial header that we peeked at before
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(sizeof(ZoneHeader)));
|
||||
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
|
||||
|
||||
// Start of the XFile struct
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace T5
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
|
||||
@@ -47,8 +47,9 @@ namespace
|
||||
ZoneCompressionTypeT6 m_compression_type;
|
||||
};
|
||||
|
||||
std::optional<ZoneLoaderInspectionResultT6> InspectZoneHeaderT6(const ZoneHeader& header)
|
||||
std::optional<ZoneLoaderInspectionResultT6> InspectZoneHeaderT6(ZoneDataPeeking& filePeek)
|
||||
{
|
||||
const auto& header = filePeek.PeekStruct<ZoneHeader>();
|
||||
if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC)
|
||||
{
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8))
|
||||
@@ -304,20 +305,20 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(ZoneDataPeeking& filePeek) const
|
||||
{
|
||||
auto resultT6 = InspectZoneHeaderT6(header);
|
||||
auto resultT6 = InspectZoneHeaderT6(filePeek);
|
||||
if (!resultT6)
|
||||
return std::nullopt;
|
||||
|
||||
return resultT6->m_generic_result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeaderT6(header);
|
||||
const auto inspectResult = InspectZoneHeaderT6(filePeek);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
@@ -331,6 +332,9 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
|
||||
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// Skip the initial header that we peeked at before
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(sizeof(ZoneHeader)));
|
||||
|
||||
// If file is signed setup a RSA instance.
|
||||
auto rsa = inspectResult->m_generic_result.m_is_signed ? SetupRsa(inspectResult->m_generic_result.m_is_official) : nullptr;
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace T6
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Game/IW3/ZoneLoaderFactoryIW3.h"
|
||||
#include "Game/IW4/ZoneLoaderFactoryIW4.h"
|
||||
#include "Game/IW5/ZoneLoaderFactoryIW5.h"
|
||||
#include "Game/QOS/ZoneLoaderFactoryQOS.h"
|
||||
#include "Game/T4/ZoneLoaderFactoryT4.h"
|
||||
#include "Game/T5/ZoneLoaderFactoryT5.h"
|
||||
#include "Game/T6/ZoneLoaderFactoryT6.h"
|
||||
@@ -15,6 +16,7 @@ const IZoneLoaderFactory* IZoneLoaderFactory::GetZoneLoaderFactoryForGame(GameId
|
||||
new IW3::ZoneLoaderFactory(),
|
||||
new IW4::ZoneLoaderFactory(),
|
||||
new IW5::ZoneLoaderFactory(),
|
||||
new QOS::ZoneLoaderFactory(),
|
||||
new T4::ZoneLoaderFactory(),
|
||||
new T5::ZoneLoaderFactory(),
|
||||
new T6::ZoneLoaderFactory(),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Game/IGame.h"
|
||||
#include "Utils/ProgressCallback.h"
|
||||
#include "Zone/ZoneTypes.h"
|
||||
#include "ZoneDataPeeking.h"
|
||||
#include "ZoneLoader.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -36,8 +37,8 @@ public:
|
||||
IZoneLoaderFactory& operator=(const IZoneLoaderFactory& other) = default;
|
||||
IZoneLoaderFactory& operator=(IZoneLoaderFactory&& other) noexcept = default;
|
||||
|
||||
[[nodiscard]] virtual std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
[[nodiscard]] virtual std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(ZoneDataPeeking& filePeek) const = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneDataPeeking& filePeek,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const = 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "ZoneDataPeeking.h"
|
||||
|
||||
ZoneDataPeeking::ZoneDataPeeking(std::istream& stream)
|
||||
: m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
void ZoneDataPeeking::EnsureHasEnoughData(const size_t size)
|
||||
{
|
||||
const auto currentSize = m_data.size();
|
||||
if (size > currentSize)
|
||||
{
|
||||
m_data.resize(size);
|
||||
m_stream.read(&m_data[currentSize], size - currentSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <istream>
|
||||
#include <vector>
|
||||
|
||||
class ZoneDataPeeking
|
||||
{
|
||||
public:
|
||||
explicit ZoneDataPeeking(std::istream& stream);
|
||||
|
||||
template<typename T> const T& PeekStruct()
|
||||
{
|
||||
EnsureHasEnoughData(sizeof(T));
|
||||
|
||||
return *reinterpret_cast<const T*>(m_data.data());
|
||||
}
|
||||
|
||||
private:
|
||||
void EnsureHasEnoughData(size_t size);
|
||||
|
||||
std::istream& m_stream;
|
||||
std::vector<char> m_data;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include "Loading/IZoneLoaderFactory.h"
|
||||
#include "Loading/ZoneDataPeeking.h"
|
||||
#include "Loading/ZoneLoader.h"
|
||||
|
||||
#include <filesystem>
|
||||
@@ -20,18 +21,14 @@ std::expected<std::unique_ptr<Zone>, std::string> ZoneLoading::LoadZone(const st
|
||||
if (!file.is_open())
|
||||
return std::unexpected(std::format("Could not open file '{}'.", path));
|
||||
|
||||
ZoneHeader header{};
|
||||
file.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (file.gcount() != sizeof(header))
|
||||
return std::unexpected(std::format("Failed to read zone header from file '{}'.", path));
|
||||
|
||||
ZoneDataPeeking dataPeeking(file);
|
||||
std::unique_ptr<ZoneLoader> zoneLoader;
|
||||
for (auto game = 0u; game < static_cast<unsigned>(GameId::COUNT); game++)
|
||||
{
|
||||
const auto* factory = IZoneLoaderFactory::GetZoneLoaderFactoryForGame(static_cast<GameId>(game));
|
||||
if (factory->InspectZoneHeader(header))
|
||||
if (factory->InspectZoneHeader(dataPeeking))
|
||||
{
|
||||
zoneLoader = factory->CreateLoaderForHeader(header, zoneName, std::move(progressCallback));
|
||||
zoneLoader = factory->CreateLoaderForHeader(dataPeeking, zoneName, std::move(progressCallback));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -39,6 +36,10 @@ std::expected<std::unique_ptr<Zone>, std::string> ZoneLoading::LoadZone(const st
|
||||
if (!zoneLoader)
|
||||
return std::unexpected(std::format("Could not create factory for zone '{}'.", zoneName));
|
||||
|
||||
// Revert to beginning to make sure no peeking has advanced the read position.
|
||||
// (As different peeking logic may advance by different amounts of bytes).
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
auto loadedZone = zoneLoader->LoadZone(file);
|
||||
|
||||
file.close();
|
||||
|
||||
Reference in new issue
Block a user