mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-07-03 22:30:07 +00:00
feat: initial T4 support (#807)
* feat: initial T4 support * chore: adjust t4 symbols a bit for accuracy * chore: add PackIndex asset to T4 * chore: remove unused AssetXModelPieces * chore: add default and global asset pools loader for T4 * chore: use separate defines for T4 in ImageDumper * chore: remove unnecessary namespaces in gfximage_actions * chore: small things * chore: fix T4 PhysPreset type * chore: use proper XQuat2 type for T4 xanims * chore: fix errors on T4 types * chore: use iw3 like struct for XModelStreamInfo * docs: add basic docs for T4 * chore: add basic ObjCompiler setup for T4 * chore: adjust loaded sound definition * chore: make sure t4 material has the correct alignment * chore: make sure t4 uses similar names for assets as other games * fix: asset references should not be reusable * chore: add content writer for t4 * feat: add t4 localize loader * chore: reorder game ids to be alphabetically ordered --------- Co-authored-by: Jan Laupetin <jan@laupetin.net>
This commit is contained in:
@@ -17,7 +17,7 @@ void Actions_GfxImage::OnImageLoaded(GfxImage* image) const
|
||||
|
||||
void Actions_GfxImage::LoadImageData(GfxImageLoadDef* loadDef, GfxImage* image) const
|
||||
{
|
||||
const size_t loadDefSize = offsetof(IW3::GfxImageLoadDef, data) + loadDef->resourceSize;
|
||||
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);
|
||||
|
||||
@@ -17,7 +17,7 @@ void Actions_GfxImage::OnImageLoaded(GfxImage* image) const
|
||||
|
||||
void Actions_GfxImage::LoadImageData(GfxImageLoadDef* loadDef, GfxImage* image) const
|
||||
{
|
||||
const size_t loadDefSize = offsetof(IW4::GfxImageLoadDef, data) + loadDef->resourceSize;
|
||||
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);
|
||||
|
||||
@@ -17,7 +17,7 @@ void Actions_GfxImage::OnImageLoaded(GfxImage* image) const
|
||||
|
||||
void Actions_GfxImage::LoadImageData(GfxImageLoadDef* loadDef, GfxImage* image) const
|
||||
{
|
||||
const size_t loadDefSize = offsetof(IW5::GfxImageLoadDef, data) + loadDef->resourceSize;
|
||||
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,174 @@
|
||||
#include "ContentLoaderT4.h"
|
||||
|
||||
#include "Game/T4/AssetLoaderT4.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "Loading/Exception/UnsupportedAssetTypeException.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace T4;
|
||||
|
||||
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; \
|
||||
}
|
||||
#define SKIP_ASSET(type_index, typeName, headerEntry) \
|
||||
case type_index: \
|
||||
break;
|
||||
|
||||
assert(varXAsset != nullptr);
|
||||
|
||||
if (atStreamStart)
|
||||
m_stream.Load<XAsset>(varXAsset);
|
||||
|
||||
switch (varXAsset->type)
|
||||
{
|
||||
SKIP_ASSET(ASSET_TYPE_XMODELPIECES, XModelPieces, data)
|
||||
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_LOADED_SOUND, LoadedSound, loadSnd)
|
||||
LOAD_ASSET(ASSET_TYPE_CLIPMAP, clipMap_t, clipMap)
|
||||
LOAD_ASSET(ASSET_TYPE_CLIPMAP_PVS, 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)
|
||||
SKIP_ASSET(ASSET_TYPE_UI_MAP, UiMap, data)
|
||||
LOAD_ASSET(ASSET_TYPE_FONT, Font_s, font)
|
||||
LOAD_ASSET(ASSET_TYPE_MENULIST, 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_SNDDRIVER_GLOBALS, SndDriverGlobals, sndDriverGlobals)
|
||||
LOAD_ASSET(ASSET_TYPE_FX, FxEffectDef, fx)
|
||||
LOAD_ASSET(ASSET_TYPE_IMPACT_FX, FxImpactTable, impactFx)
|
||||
SKIP_ASSET(ASSET_TYPE_AITYPE, AiType, data)
|
||||
SKIP_ASSET(ASSET_TYPE_MPTYPE, MpType, data)
|
||||
SKIP_ASSET(ASSET_TYPE_CHARACTER, Character, data)
|
||||
SKIP_ASSET(ASSET_TYPE_XMODELALIAS, XModelAlias, data)
|
||||
LOAD_ASSET(ASSET_TYPE_RAWFILE, RawFile, rawfile)
|
||||
LOAD_ASSET(ASSET_TYPE_STRINGTABLE, StringTable, stringTable)
|
||||
LOAD_ASSET(ASSET_TYPE_PACK_INDEX, PackIndex, packIndex)
|
||||
|
||||
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/T4/T4.h"
|
||||
#include "Loading/ContentLoaderBase.h"
|
||||
#include "Loading/IContentLoadingEntryPoint.h"
|
||||
|
||||
namespace T4
|
||||
{
|
||||
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 T4
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "gfximage_actions.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
using namespace T4;
|
||||
|
||||
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/T4/T4.h"
|
||||
#include "Loading/AssetLoadingActions.h"
|
||||
|
||||
namespace T4
|
||||
{
|
||||
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 T4
|
||||
@@ -0,0 +1,89 @@
|
||||
#include "ZoneLoaderFactoryT4.h"
|
||||
|
||||
#include "ContentLoaderT4.h"
|
||||
#include "Game/GameLanguage.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "Game/T4/ZoneConstantsT4.h"
|
||||
#include "Loading/Processor/ProcessorInflate.h"
|
||||
#include "Loading/Steps/StepAddProcessor.h"
|
||||
#include "Loading/Steps/StepAllocXBlocks.h"
|
||||
#include "Loading/Steps/StepLoadZoneContent.h"
|
||||
#include "Loading/Steps/StepLoadZoneSizes.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/Endianness.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace T4;
|
||||
|
||||
namespace
|
||||
{
|
||||
void SetupBlock(ZoneLoader& zoneLoader)
|
||||
{
|
||||
#define XBLOCK_DEF(name, type) std::make_unique<XBlock>(STR(name), name, type)
|
||||
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(T4::XFILE_BLOCK_TEMP, XBlockType::BLOCK_TYPE_TEMP));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(T4::XFILE_BLOCK_RUNTIME, XBlockType::BLOCK_TYPE_RUNTIME));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(T4::XFILE_BLOCK_LARGE_RUNTIME, XBlockType::BLOCK_TYPE_RUNTIME));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(T4::XFILE_BLOCK_PHYSICAL_RUNTIME, XBlockType::BLOCK_TYPE_RUNTIME));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(T4::XFILE_BLOCK_VIRTUAL, XBlockType::BLOCK_TYPE_NORMAL));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(T4::XFILE_BLOCK_LARGE, XBlockType::BLOCK_TYPE_NORMAL));
|
||||
zoneLoader.AddXBlock(XBLOCK_DEF(T4::XFILE_BLOCK_PHYSICAL, XBlockType::BLOCK_TYPE_NORMAL));
|
||||
|
||||
#undef XBLOCK_DEF
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
{
|
||||
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)))
|
||||
{
|
||||
return ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T4,
|
||||
.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(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
// Create new zone
|
||||
auto zone = std::make_unique<Zone>(fileName, 0, GameId::T4, inspectResult->m_platform);
|
||||
auto* zonePtr = zone.get();
|
||||
zone->m_language = GameLanguage::LANGUAGE_NONE;
|
||||
|
||||
// File is supported. Now setup all required steps for loading this file.
|
||||
auto zoneLoader = std::make_unique<ZoneLoader>(std::move(zone));
|
||||
|
||||
SetupBlock(*zoneLoader);
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
|
||||
zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes());
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAllocXBlocks());
|
||||
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 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,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
} // namespace T4
|
||||
@@ -17,7 +17,7 @@ void Actions_GfxImage::OnImageLoaded(GfxImage* image) const
|
||||
|
||||
void Actions_GfxImage::LoadImageData(GfxImageLoadDef* loadDef, GfxImage* image) const
|
||||
{
|
||||
const size_t loadDefSize = offsetof(T6::GfxImageLoadDef, data) + loadDef->resourceSize;
|
||||
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);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Game/IW3/ZoneLoaderFactoryIW3.h"
|
||||
#include "Game/IW4/ZoneLoaderFactoryIW4.h"
|
||||
#include "Game/IW5/ZoneLoaderFactoryIW5.h"
|
||||
#include "Game/T4/ZoneLoaderFactoryT4.h"
|
||||
#include "Game/T5/ZoneLoaderFactoryT5.h"
|
||||
#include "Game/T6/ZoneLoaderFactoryT6.h"
|
||||
|
||||
@@ -10,10 +11,11 @@
|
||||
|
||||
const IZoneLoaderFactory* IZoneLoaderFactory::GetZoneLoaderFactoryForGame(GameId game)
|
||||
{
|
||||
static const IZoneLoaderFactory* zoneCreators[static_cast<unsigned>(GameId::COUNT)]{
|
||||
static const IZoneLoaderFactory* zoneCreators[]{
|
||||
new IW3::ZoneLoaderFactory(),
|
||||
new IW4::ZoneLoaderFactory(),
|
||||
new IW5::ZoneLoaderFactory(),
|
||||
new T4::ZoneLoaderFactory(),
|
||||
new T5::ZoneLoaderFactory(),
|
||||
new T6::ZoneLoaderFactory(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user