diff --git a/src/Common/Game/IGame.h b/src/Common/Game/IGame.h index 02519c3a..eae862f7 100644 --- a/src/Common/Game/IGame.h +++ b/src/Common/Game/IGame.h @@ -1,13 +1,21 @@ #pragma once -#include "GameLanguage.h" #include +#include "GameLanguage.h" class Zone; class IGame { public: - virtual const std::string& GetName() = 0; + IGame() = default; + virtual ~IGame() = default; + IGame(const IGame& other) = default; + IGame(IGame&& other) noexcept = default; + IGame& operator=(const IGame& other) = default; + IGame& operator=(IGame&& other) noexcept = default; + + virtual std::string GetFullName() = 0; + virtual std::string GetShortName() = 0; virtual void AddZone(Zone* zone) = 0; virtual void RemoveZone(Zone* zone) = 0; virtual std::vector GetZones() = 0; diff --git a/src/Common/Game/IW4/GameIW4.cpp b/src/Common/Game/IW4/GameIW4.cpp index c642edc2..76d22521 100644 --- a/src/Common/Game/IW4/GameIW4.cpp +++ b/src/Common/Game/IW4/GameIW4.cpp @@ -8,11 +8,14 @@ using namespace IW4; GameIW4 g_GameIW4; -const std::string GameIW4::NAME = "IW4"; - -const std::string& GameIW4::GetName() +std::string GameIW4::GetFullName() { - return NAME; + return "Call Of Duty: Modern Warfare 2"; +} + +std::string GameIW4::GetShortName() +{ + return "IW4"; } void GameIW4::AddZone(Zone* zone) diff --git a/src/Common/Game/IW4/GameIW4.h b/src/Common/Game/IW4/GameIW4.h index 920a55d9..26a9b79e 100644 --- a/src/Common/Game/IW4/GameIW4.h +++ b/src/Common/Game/IW4/GameIW4.h @@ -3,11 +3,11 @@ class GameIW4 : public IGame { - static const std::string NAME; std::vector m_zones; public: - const std::string& GetName() override; + std::string GetFullName() override; + std::string GetShortName() override; void AddZone(Zone* zone) override; void RemoveZone(Zone* zone) override; std::vector GetZones() override; diff --git a/src/Common/Game/T6/GameT6.cpp b/src/Common/Game/T6/GameT6.cpp index d1e2cd8b..e81f1552 100644 --- a/src/Common/Game/T6/GameT6.cpp +++ b/src/Common/Game/T6/GameT6.cpp @@ -8,11 +8,14 @@ using namespace T6; GameT6 g_GameT6; -const std::string GameT6::NAME = "T6"; - -const std::string& GameT6::GetName() +std::string GameT6::GetFullName() { - return NAME; + return "Call Of Duty: Black Ops II"; +} + +std::string GameT6::GetShortName() +{ + return "T6"; } void GameT6::AddZone(Zone* zone) diff --git a/src/Common/Game/T6/GameT6.h b/src/Common/Game/T6/GameT6.h index b28544f5..60ce25d1 100644 --- a/src/Common/Game/T6/GameT6.h +++ b/src/Common/Game/T6/GameT6.h @@ -3,11 +3,11 @@ class GameT6 : public IGame { - static const std::string NAME; std::vector m_zones; public: - const std::string& GetName() override; + std::string GetFullName() override; + std::string GetShortName() override; void AddZone(Zone* zone) override; void RemoveZone(Zone* zone) override; std::vector GetZones() override; diff --git a/src/Unlinker/ContentLister/ContentPrinter.cpp b/src/Unlinker/ContentLister/ContentPrinter.cpp index 9350f381..093e232e 100644 --- a/src/Unlinker/ContentLister/ContentPrinter.cpp +++ b/src/Unlinker/ContentLister/ContentPrinter.cpp @@ -8,7 +8,7 @@ ContentPrinter::ContentPrinter(Zone* zone) void ContentPrinter::PrintContent() const { const auto* pools = m_zone->m_pools.get(); - printf("Zone '%s' (%s)\n", m_zone->m_name.c_str(), m_zone->m_game->GetName().c_str()); + printf("Zone '%s' (%s)\n", m_zone->m_name.c_str(), m_zone->m_game->GetShortName().c_str()); puts("Content:"); for(const auto& asset : *pools) diff --git a/src/Unlinker/ContentLister/ZoneDefWriter.cpp b/src/Unlinker/ContentLister/ZoneDefWriter.cpp index 4cf15d6a..76168546 100644 --- a/src/Unlinker/ContentLister/ZoneDefWriter.cpp +++ b/src/Unlinker/ContentLister/ZoneDefWriter.cpp @@ -1,29 +1,13 @@ #include "ZoneDefWriter.h" -const std::string AbstractZoneDefWriter::META_DATA_KEY_GAME = "game"; - -AbstractZoneDefWriter::AbstractZoneDefWriter(Zone* zone, std::ostream& stream) - : m_zone(zone), - m_stream(stream) +void AbstractZoneDefWriter::WriteZoneDef(std::ostream& stream, Zone* zone) const { -} + ZoneDefinitionOutputStream out(stream); -void AbstractZoneDefWriter::EmptyLine() const -{ - m_stream << "\n"; -} + out.WriteComment(zone->m_game->GetFullName()); + out.WriteMetaData(META_DATA_KEY_GAME, zone->m_game->GetShortName()); + out.EmptyLine(); -void AbstractZoneDefWriter::WriteComment(const std::string& comment) const -{ - m_stream << "// " << comment << "\n"; -} - -void AbstractZoneDefWriter::WriteMetaData(const std::string& metaDataKey, const std::string& metaDataValue) const -{ - m_stream << ">" << metaDataKey << "," << metaDataValue << "\n"; -} - -void AbstractZoneDefWriter::WriteEntry(const std::string& entryKey, const std::string& entryValue) const -{ - m_stream << entryKey << "," << entryValue << "\n"; -} + WriteMetaData(out, zone); + WriteContent(out, zone); +} \ No newline at end of file diff --git a/src/Unlinker/ContentLister/ZoneDefWriter.h b/src/Unlinker/ContentLister/ZoneDefWriter.h index 88bbce50..50f12708 100644 --- a/src/Unlinker/ContentLister/ZoneDefWriter.h +++ b/src/Unlinker/ContentLister/ZoneDefWriter.h @@ -1,31 +1,30 @@ #pragma once -#include - #include "Zone/Zone.h" - -class AbstractZoneDefWriter -{ -protected: - Zone* m_zone; - std::ostream& m_stream; - - static const std::string META_DATA_KEY_GAME; - - void EmptyLine() const; - void WriteComment(const std::string& comment) const; - void WriteMetaData(const std::string& metaDataKey, const std::string& metaDataValue) const; - void WriteEntry(const std::string& entryKey, const std::string& entryValue) const; - - AbstractZoneDefWriter(Zone* zone, std::ostream& stream); - -public: - virtual void WriteZoneDef() = 0; -}; +#include "Zone/Definition/ZoneDefinitionStream.h" class IZoneDefWriter { public: + IZoneDefWriter() = default; + virtual ~IZoneDefWriter() = default; + IZoneDefWriter(const IZoneDefWriter& other) = default; + IZoneDefWriter(IZoneDefWriter&& other) noexcept = default; + IZoneDefWriter& operator=(const IZoneDefWriter& other) = default; + IZoneDefWriter& operator=(IZoneDefWriter&& other) noexcept = default; + virtual bool CanHandleZone(Zone* zone) const = 0; - virtual void WriteZoneDef(Zone* zone, std::ostream& stream) const = 0; + virtual void WriteZoneDef(std::ostream& stream, Zone* zone) const = 0; +}; + +class AbstractZoneDefWriter : public IZoneDefWriter +{ +protected: + static constexpr const char* META_DATA_KEY_GAME = "game"; + + virtual void WriteMetaData(ZoneDefinitionOutputStream& stream, Zone* zone) const = 0; + virtual void WriteContent(ZoneDefinitionOutputStream& stream, Zone* zone) const = 0; + +public: + void WriteZoneDef(std::ostream& stream, Zone* zone) const override; }; \ No newline at end of file diff --git a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.cpp b/src/Unlinker/Game/IW4/ZoneDefWriterIW4.cpp index b7c65dfc..56c90076 100644 --- a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.cpp +++ b/src/Unlinker/Game/IW4/ZoneDefWriterIW4.cpp @@ -1,70 +1,45 @@ #include "ZoneDefWriterIW4.h" -#include "Game/IW4/GameIW4.h" -#include "Game/IW4/CommonIW4.h" -#include "Game/IW4/GameAssetPoolIW4.h" -#include -#include #include +#include "Game/IW4/GameIW4.h" +#include "Game/IW4/GameAssetPoolIW4.h" + using namespace IW4; -namespace IW4 -{ - class ZoneDefWriterInternal final : public AbstractZoneDefWriter - { - void WriteContent() const - { - const auto* pools = dynamic_cast(m_zone->m_pools.get()); - - assert(pools); - if (!pools) - return; - - // Localized strings are all collected in one string file. So only add this to the zone file. - if (!pools->m_localize->m_asset_lookup.empty()) - { - WriteEntry(pools->GetAssetTypeName(ASSET_TYPE_LOCALIZE_ENTRY), m_zone->m_name); - } - - for (const auto& asset : *pools) - { - switch (asset->m_type) - { - case ASSET_TYPE_LOCALIZE_ENTRY: - break; - - default: - WriteEntry(pools->GetAssetTypeName(asset->m_type), asset->m_name); - break; - } - } - } - - public: - ZoneDefWriterInternal(Zone* zone, std::ostream& stream) - : AbstractZoneDefWriter(zone, stream) - { - } - - void WriteZoneDef() override - { - WriteComment("Call Of Duty: Modern Warfare 2"); - WriteMetaData(META_DATA_KEY_GAME, "iw4"); - EmptyLine(); - - WriteContent(); - } - }; -} - bool ZoneDefWriter::CanHandleZone(Zone* zone) const { return zone->m_game == &g_GameIW4; } -void ZoneDefWriter::WriteZoneDef(Zone* zone, std::ostream& stream) const +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, Zone* zone) const { - ZoneDefWriterInternal writer(zone, stream); - writer.WriteZoneDef(); +} + +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, Zone* zone) const +{ + const auto* pools = dynamic_cast(zone->m_pools.get()); + + assert(pools); + if (!pools) + return; + + // Localized strings are all collected in one string file. So only add this to the zone file. + if (!pools->m_localize->m_asset_lookup.empty()) + { + stream.WriteEntry(pools->GetAssetTypeName(ASSET_TYPE_LOCALIZE_ENTRY), zone->m_name); + } + + for (const auto& asset : *pools) + { + switch (asset->m_type) + { + case ASSET_TYPE_LOCALIZE_ENTRY: + break; + + default: + stream.WriteEntry(pools->GetAssetTypeName(asset->m_type), asset->m_name); + break; + } + } } diff --git a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.h b/src/Unlinker/Game/IW4/ZoneDefWriterIW4.h index 750d2cb1..60b6114f 100644 --- a/src/Unlinker/Game/IW4/ZoneDefWriterIW4.h +++ b/src/Unlinker/Game/IW4/ZoneDefWriterIW4.h @@ -4,10 +4,13 @@ namespace IW4 { - class ZoneDefWriter final : public IZoneDefWriter + class ZoneDefWriter final : public AbstractZoneDefWriter { + protected: + void WriteMetaData(::ZoneDefinitionOutputStream& stream, ::Zone* zone) const override; + void WriteContent(::ZoneDefinitionOutputStream& stream, ::Zone* zone) const override; + public: bool CanHandleZone(Zone* zone) const override; - void WriteZoneDef(Zone* zone, std::ostream& stream) const override; }; } \ No newline at end of file diff --git a/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp b/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp index 067879ac..d5f197e3 100644 --- a/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp +++ b/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp @@ -11,104 +11,25 @@ using namespace T6; namespace T6 { - class ZoneDefWriterInternal final : public AbstractZoneDefWriter + class KeyValuePairKnownKey { - class KnownKey - { - public: - std::string m_key; - int m_hash; - - explicit KnownKey(std::string key) - { - m_key = std::move(key); - m_hash = CommonT6::Com_HashKey(m_key.c_str(), 64); - } - }; - - inline static const KnownKey KNOWN_KEYS[] - { - KnownKey("ipak_read"), - KnownKey("ipak_write"), - KnownKey("initial_xmodels"), - KnownKey("initial_materials"), - }; - - void WriteKeyValuePair(KeyValuePair* kvp) const - { - for (const auto& knownKey : KNOWN_KEYS) - { - if (knownKey.m_hash == kvp->keyHash) - { - WriteMetaData("level." + knownKey.m_key, kvp->value); - return; - } - } - - std::ostringstream str; - str << "level.@" << std::setfill('0') << std::setw(sizeof(int) * 2) << std::hex << kvp->keyHash; - WriteMetaData(str.str(), kvp->value); - } - - void WriteContent() const - { - const auto* pools = dynamic_cast(m_zone->m_pools.get()); - - assert(pools); - if (!pools) - return; - - // Localized strings are all collected in one string file. So only add this to the zone file. - if (!pools->m_localize->m_asset_lookup.empty()) - { - WriteEntry(pools->GetAssetTypeName(ASSET_TYPE_LOCALIZE_ENTRY), m_zone->m_name); - } - - for (const auto& asset : *pools) - { - switch (asset->m_type) - { - case ASSET_TYPE_LOCALIZE_ENTRY: - case ASSET_TYPE_KEYVALUEPAIRS: // KeyValuePairs should be included as zone file metadata and not as content - break; - - default: - WriteEntry(pools->GetAssetTypeName(asset->m_type), asset->m_name); - break; - } - } - } - public: - ZoneDefWriterInternal(Zone* zone, std::ostream& stream) - : AbstractZoneDefWriter(zone, stream) + std::string m_key; + int m_hash; + + explicit KeyValuePairKnownKey(std::string key) { + m_key = std::move(key); + m_hash = CommonT6::Com_HashKey(m_key.c_str(), 64); } + }; - void WriteZoneDef() override - { - WriteComment("Call Of Duty: Black Ops II"); - WriteMetaData(META_DATA_KEY_GAME, "t6"); - EmptyLine(); - - auto* assetPoolT6 = dynamic_cast(m_zone->m_pools.get()); - - if (assetPoolT6 && !assetPoolT6->m_key_value_pairs->m_asset_lookup.empty()) - { - for (auto kvpAsset : *assetPoolT6->m_key_value_pairs) - { - KeyValuePairs* keyValuePairs = kvpAsset->Asset(); - for (int varIndex = 0; varIndex < keyValuePairs->numVariables; varIndex++) - { - WriteKeyValuePair(&keyValuePairs->keyValuePairs[varIndex]); - } - } - - EmptyLine(); - } - - WriteContent(); - } + const KeyValuePairKnownKey KEY_VALUE_PAIR_KNOWN_KEYS[] + { + KeyValuePairKnownKey("ipak_read"), + KeyValuePairKnownKey("ipak_write"), + KeyValuePairKnownKey("initial_xmodels"), + KeyValuePairKnownKey("initial_materials"), }; } @@ -117,8 +38,66 @@ bool ZoneDefWriter::CanHandleZone(Zone* zone) const return zone->m_game == &g_GameT6; } -void ZoneDefWriter::WriteZoneDef(Zone* zone, std::ostream& stream) const +void ZoneDefWriter::WriteKeyValuePair(ZoneDefinitionOutputStream& stream, KeyValuePair* kvp) { - ZoneDefWriterInternal writer(zone, stream); - writer.WriteZoneDef(); + for (const auto& knownKey : KEY_VALUE_PAIR_KNOWN_KEYS) + { + if (knownKey.m_hash == kvp->keyHash) + { + stream.WriteMetaData("level." + knownKey.m_key, kvp->value); + return; + } + } + + std::ostringstream str; + str << "level.@" << std::setfill('0') << std::setw(sizeof(int) * 2) << std::hex << kvp->keyHash; + stream.WriteMetaData(str.str(), kvp->value); } + +void ZoneDefWriter::WriteMetaData(ZoneDefinitionOutputStream& stream, Zone* zone) const +{ + auto* assetPoolT6 = dynamic_cast(zone->m_pools.get()); + + if (assetPoolT6 && !assetPoolT6->m_key_value_pairs->m_asset_lookup.empty()) + { + for (const auto* kvpAsset : *assetPoolT6->m_key_value_pairs) + { + const auto* keyValuePairs = kvpAsset->Asset(); + for (auto varIndex = 0; varIndex < keyValuePairs->numVariables; varIndex++) + { + WriteKeyValuePair(stream, &keyValuePairs->keyValuePairs[varIndex]); + } + } + + stream.EmptyLine(); + } +} + +void ZoneDefWriter::WriteContent(ZoneDefinitionOutputStream& stream, Zone* zone) const +{ + const auto* pools = dynamic_cast(zone->m_pools.get()); + + assert(pools); + if (!pools) + return; + + // Localized strings are all collected in one string file. So only add this to the zone file. + if (!pools->m_localize->m_asset_lookup.empty()) + { + stream.WriteEntry(pools->GetAssetTypeName(ASSET_TYPE_LOCALIZE_ENTRY), zone->m_name); + } + + for (const auto& asset : *pools) + { + switch (asset->m_type) + { + case ASSET_TYPE_LOCALIZE_ENTRY: + case ASSET_TYPE_KEYVALUEPAIRS: // KeyValuePairs should be included as zone file metadata and not as content + break; + + default: + stream.WriteEntry(pools->GetAssetTypeName(asset->m_type), asset->m_name); + break; + } + } +} \ No newline at end of file diff --git a/src/Unlinker/Game/T6/ZoneDefWriterT6.h b/src/Unlinker/Game/T6/ZoneDefWriterT6.h index d12e65dc..95f7cd30 100644 --- a/src/Unlinker/Game/T6/ZoneDefWriterT6.h +++ b/src/Unlinker/Game/T6/ZoneDefWriterT6.h @@ -1,13 +1,19 @@ #pragma once #include "ContentLister/ZoneDefWriter.h" +#include "Game/T6/T6.h" namespace T6 { - class ZoneDefWriter final : public IZoneDefWriter + class ZoneDefWriter final : public AbstractZoneDefWriter { + static void WriteKeyValuePair(ZoneDefinitionOutputStream& stream, KeyValuePair* kvp); + + protected: + void WriteMetaData(ZoneDefinitionOutputStream& stream, Zone* zone) const override; + void WriteContent(ZoneDefinitionOutputStream& stream, Zone* zone) const override; + public: bool CanHandleZone(Zone* zone) const override; - void WriteZoneDef(Zone* zone, std::ostream& stream) const override; }; -} \ No newline at end of file +} diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index 2247d6e2..d3710341 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -168,7 +168,7 @@ class Unlinker::Impl { if (zoneDefWriter->CanHandleZone(zone)) { - zoneDefWriter->WriteZoneDef(zone, zoneDefinitionFile); + zoneDefWriter->WriteZoneDef(zoneDefinitionFile, zone); result = true; break; } @@ -234,7 +234,7 @@ class Unlinker::Impl return false; auto gdt = std::make_unique(gdtStream); gdt->BeginStream(); - gdt->WriteVersion(GdtVersion(zone->m_game->GetName(), 1)); + gdt->WriteVersion(GdtVersion(zone->m_game->GetShortName(), 1)); context.m_gdt = std::move(gdt); } diff --git a/src/ZoneCommon/Pool/XAssetInfo.h b/src/ZoneCommon/Pool/XAssetInfo.h index c7a2e70a..d95b3095 100644 --- a/src/ZoneCommon/Pool/XAssetInfo.h +++ b/src/ZoneCommon/Pool/XAssetInfo.h @@ -24,4 +24,9 @@ public: { return static_cast(m_ptr); } + + const T* Asset() const + { + return static_cast(const_cast(m_ptr)); + } }; diff --git a/src/ZoneCommon/Zone/Definition/ZoneDefinition.cpp b/src/ZoneCommon/Zone/Definition/ZoneDefinition.cpp new file mode 100644 index 00000000..821285e2 --- /dev/null +++ b/src/ZoneCommon/Zone/Definition/ZoneDefinition.cpp @@ -0,0 +1,13 @@ +#include "ZoneDefinition.h" + +ZoneDefinitionEntry::ZoneDefinitionEntry() + : m_is_reference(false) +{ +} + +ZoneDefinitionEntry::ZoneDefinitionEntry(std::string type, std::string name, bool isReference) + : m_asset_type(std::move(type)), + m_asset_name(std::move(name)), + m_is_reference(isReference) +{ +} diff --git a/src/ZoneCommon/Zone/Definition/ZoneDefinition.h b/src/ZoneCommon/Zone/Definition/ZoneDefinition.h new file mode 100644 index 00000000..4e9b410f --- /dev/null +++ b/src/ZoneCommon/Zone/Definition/ZoneDefinition.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +class ZoneDefinitionEntry +{ +public: + std::string m_asset_type; + std::string m_asset_name; + bool m_is_reference; + + ZoneDefinitionEntry(); + ZoneDefinitionEntry(std::string type, std::string name, bool isReference); +}; + +class ZoneDefinition +{ +public: + std::unordered_map m_metadata; + std::vector m_assets; +}; \ No newline at end of file diff --git a/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp new file mode 100644 index 00000000..20ea1760 --- /dev/null +++ b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.cpp @@ -0,0 +1,36 @@ +#include "ZoneDefinitionStream.h" + +ZoneDefinitionInputStream::ZoneDefinitionInputStream(std::istream& stream) + : m_stream(stream) +{ +} + +std::unique_ptr ZoneDefinitionInputStream::ReadDefinition() +{ + return nullptr; +} + +ZoneDefinitionOutputStream::ZoneDefinitionOutputStream(std::ostream& stream) + : m_stream(stream) +{ +} + +void ZoneDefinitionOutputStream::EmptyLine() const +{ + m_stream << "\n"; +} + +void ZoneDefinitionOutputStream::WriteComment(const std::string& comment) const +{ + m_stream << "// " << comment << "\n"; +} + +void ZoneDefinitionOutputStream::WriteMetaData(const std::string& metaDataKey, const std::string& metaDataValue) const +{ + m_stream << ">" << metaDataKey << "," << metaDataValue << "\n"; +} + +void ZoneDefinitionOutputStream::WriteEntry(const std::string& entryKey, const std::string& entryValue) const +{ + m_stream << entryKey << "," << entryValue << "\n"; +} diff --git a/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.h b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.h new file mode 100644 index 00000000..01ff04a4 --- /dev/null +++ b/src/ZoneCommon/Zone/Definition/ZoneDefinitionStream.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include + +#include "ZoneDefinition.h" + +class ZoneDefinitionInputStream +{ + std::istream& m_stream; + +public: + explicit ZoneDefinitionInputStream(std::istream& stream); + + std::unique_ptr ReadDefinition(); +}; + +class ZoneDefinitionOutputStream +{ + std::ostream& m_stream; + +public: + explicit ZoneDefinitionOutputStream(std::ostream& stream); + + void EmptyLine() const; + void WriteComment(const std::string& comment) const; + void WriteMetaData(const std::string& metaDataKey, const std::string& metaDataValue) const; + void WriteEntry(const std::string& entryKey, const std::string& entryValue) const; +}; \ No newline at end of file