From 95723910821483c60613013b6d7bf0e2452fe424 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 18 Feb 2020 17:15:51 +0100 Subject: [PATCH] Unlinker: Make zone files creators game dependent and in the unlinker project instead of the ObjWriting component --- src/ObjLoading/Game/T6/ObjLoaderT6.cpp | 24 +---- src/ObjLoading/Game/T6/ObjLoaderT6.h | 1 - src/ObjWriting/Dumping/IZoneDumper.h | 2 - src/ObjWriting/Game/T6/ZoneDumperT6.cpp | 5 - src/ObjWriting/Game/T6/ZoneDumperT6.h | 1 - src/ObjWriting/ObjWriting.cpp | 5 - src/ObjWriting/ObjWriting.h | 3 - .../{ => ContentLister}/ContentPrinter.cpp | 0 .../{ => ContentLister}/ContentPrinter.h | 0 src/Unlinker/ContentLister/ZoneDefWriter.cpp | 39 ++++++++ src/Unlinker/ContentLister/ZoneDefWriter.h | 30 ++++++ src/Unlinker/Game/T6/ZoneDefWriterT6.cpp | 91 +++++++++++++++++++ src/Unlinker/Game/T6/ZoneDefWriterT6.h | 10 ++ src/Unlinker/Unlinker.cpp | 18 +++- src/Unlinker/UnlinkerArgs.cpp | 2 +- src/Unlinker/UnlinkerArgs.h | 1 + src/ZoneCommon/Game/T6/CommonT6.cpp | 18 ++++ src/ZoneCommon/Game/T6/CommonT6.h | 7 ++ 18 files changed, 217 insertions(+), 40 deletions(-) rename src/Unlinker/{ => ContentLister}/ContentPrinter.cpp (100%) rename src/Unlinker/{ => ContentLister}/ContentPrinter.h (100%) create mode 100644 src/Unlinker/ContentLister/ZoneDefWriter.cpp create mode 100644 src/Unlinker/ContentLister/ZoneDefWriter.h create mode 100644 src/Unlinker/Game/T6/ZoneDefWriterT6.cpp create mode 100644 src/Unlinker/Game/T6/ZoneDefWriterT6.h create mode 100644 src/ZoneCommon/Game/T6/CommonT6.cpp create mode 100644 src/ZoneCommon/Game/T6/CommonT6.h diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index db824692..86c36152 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -5,26 +5,10 @@ #include "ObjLoading.h" #include "Image/Texture.h" #include "Image/IwiLoader.h" +#include "Game/T6/CommonT6.h" -const int ObjLoaderT6::IPAK_READ_HASH = Com_HashKey("ipak_read", 64); -const int ObjLoaderT6::GLOBAL_HASH = Com_HashKey("GLOBAL", 64); - -int ObjLoaderT6::Com_HashKey(const char* str, const int maxLen) -{ - if (str == nullptr) - return 0; - - int hash = 0; - for (int i = 0; i < maxLen; i++) - { - if (str[i] == '\0') - break; - - hash += str[i] * (0x77 + i); - } - - return hash ^ ((hash ^ (hash >> 10)) >> 10); -} +const int ObjLoaderT6::IPAK_READ_HASH = CommonT6::Com_HashKey("ipak_read", 64); +const int ObjLoaderT6::GLOBAL_HASH = CommonT6::Com_HashKey("GLOBAL", 64); bool ObjLoaderT6::SupportsZone(Zone* zone) const { @@ -122,7 +106,7 @@ void ObjLoaderT6::LoadCommonIPaks(ISearchPath* searchPath, Zone* zone) void ObjLoaderT6::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const { auto* assetPoolT6 = dynamic_cast(zone->GetPools()); - const int zoneNameHash = Com_HashKey(zone->m_name.c_str(), 64); + const int zoneNameHash = CommonT6::Com_HashKey(zone->m_name.c_str(), 64); LoadCommonIPaks(searchPath, zone); diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.h b/src/ObjLoading/Game/T6/ObjLoaderT6.h index c8e7989b..7972265f 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.h +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.h @@ -8,7 +8,6 @@ class ObjLoaderT6 final : public IObjLoader { static const int IPAK_READ_HASH; static const int GLOBAL_HASH; - static int Com_HashKey(const char* str, int maxLen); static void LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone); diff --git a/src/ObjWriting/Dumping/IZoneDumper.h b/src/ObjWriting/Dumping/IZoneDumper.h index 683004dc..194baa7f 100644 --- a/src/ObjWriting/Dumping/IZoneDumper.h +++ b/src/ObjWriting/Dumping/IZoneDumper.h @@ -1,7 +1,6 @@ #pragma once #include "Zone/Zone.h" -#include "Utils/FileAPI.h" class IZoneDumper { @@ -10,5 +9,4 @@ public: virtual bool CanHandleZone(Zone* zone) const = 0; virtual bool DumpZone(Zone* zone, const std::string& basePath) const = 0; - virtual bool WriteZoneDefinition(Zone* zone, FileAPI::File* file) const = 0; }; \ No newline at end of file diff --git a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp index 6d3db21d..2e487a84 100644 --- a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp +++ b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp @@ -78,9 +78,4 @@ bool ZoneDumperT6::DumpZone(Zone* zone, const std::string& basePath) const return true; #undef DUMP_ASSET_POOL -} - -bool ZoneDumperT6::WriteZoneDefinition(Zone* zone, FileAPI::File* file) const -{ - return true; } \ No newline at end of file diff --git a/src/ObjWriting/Game/T6/ZoneDumperT6.h b/src/ObjWriting/Game/T6/ZoneDumperT6.h index 84d3d399..0d5205be 100644 --- a/src/ObjWriting/Game/T6/ZoneDumperT6.h +++ b/src/ObjWriting/Game/T6/ZoneDumperT6.h @@ -6,5 +6,4 @@ class ZoneDumperT6 final : public IZoneDumper public: bool CanHandleZone(Zone* zone) const override; bool DumpZone(Zone* zone, const std::string& basePath) const override; - bool WriteZoneDefinition(Zone* zone, FileAPI::File* file) const override; }; diff --git a/src/ObjWriting/ObjWriting.cpp b/src/ObjWriting/ObjWriting.cpp index 7a918765..2f692e65 100644 --- a/src/ObjWriting/ObjWriting.cpp +++ b/src/ObjWriting/ObjWriting.cpp @@ -26,9 +26,4 @@ bool ObjWriting::DumpZone(Zone* zone, const std::string& basePath) } return false; -} - -bool ObjWriting::WriteZoneDefinition(Zone* zone, FileAPI::File* file) -{ - return file->Printf("// %s", "Insert zone definition here") > 0; } \ No newline at end of file diff --git a/src/ObjWriting/ObjWriting.h b/src/ObjWriting/ObjWriting.h index ebd57f87..e041d221 100644 --- a/src/ObjWriting/ObjWriting.h +++ b/src/ObjWriting/ObjWriting.h @@ -1,7 +1,6 @@ #pragma once #include "Zone/Zone.h" -#include "Utils/FileAPI.h" #include class ObjWriting @@ -18,10 +17,8 @@ public: bool Verbose = false; ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS; - bool MinimalZoneFileOutput = false; } Configuration; static bool DumpZone(Zone* zone, const std::string& basePath); - static bool WriteZoneDefinition(Zone* zone, FileAPI::File* file); }; diff --git a/src/Unlinker/ContentPrinter.cpp b/src/Unlinker/ContentLister/ContentPrinter.cpp similarity index 100% rename from src/Unlinker/ContentPrinter.cpp rename to src/Unlinker/ContentLister/ContentPrinter.cpp diff --git a/src/Unlinker/ContentPrinter.h b/src/Unlinker/ContentLister/ContentPrinter.h similarity index 100% rename from src/Unlinker/ContentPrinter.h rename to src/Unlinker/ContentLister/ContentPrinter.h diff --git a/src/Unlinker/ContentLister/ZoneDefWriter.cpp b/src/Unlinker/ContentLister/ZoneDefWriter.cpp new file mode 100644 index 00000000..3a28e57c --- /dev/null +++ b/src/Unlinker/ContentLister/ZoneDefWriter.cpp @@ -0,0 +1,39 @@ +#include "ZoneDefWriter.h" + +const std::string AbstractZoneDefWriter::META_DATA_KEY_GAME = "game"; + +AbstractZoneDefWriter::AbstractZoneDefWriter(Zone* zone, FileAPI::IFile* file) +{ + m_zone = zone; + m_file = file; +} + +void AbstractZoneDefWriter::EmptyLine() const +{ + m_file->Printf("\n"); +} + +void AbstractZoneDefWriter::WriteComment(const std::string& comment) const +{ + m_file->Printf("// %s\n", comment.c_str()); +} + +void AbstractZoneDefWriter::WriteMetaData(const std::string& metaDataKey, const std::string& metaDataValue) const +{ + m_file->Printf(">%s,%s\n", metaDataKey.c_str(), metaDataValue.c_str()); +} + +void AbstractZoneDefWriter::WriteEntry(const std::string& entryKey, const std::string& entryValue) const +{ + m_file->Printf("%s,%s\n", entryKey.c_str(), entryValue.c_str()); +} + +void AbstractZoneDefWriter::WriteContent() const +{ + auto zoneContent = m_zone->GetPools()->GetContent(); + + for(const auto& asset : zoneContent.m_assets) + { + WriteEntry(asset.m_asset_type_name, asset.m_asset_name); + } +} diff --git a/src/Unlinker/ContentLister/ZoneDefWriter.h b/src/Unlinker/ContentLister/ZoneDefWriter.h new file mode 100644 index 00000000..5c8f9e01 --- /dev/null +++ b/src/Unlinker/ContentLister/ZoneDefWriter.h @@ -0,0 +1,30 @@ +#pragma once +#include "Utils/FileAPI.h" +#include "Zone/Zone.h" + +class AbstractZoneDefWriter +{ +protected: + Zone* m_zone; + FileAPI::IFile* m_file; + + 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; + void WriteContent() const; + + AbstractZoneDefWriter(Zone* zone, FileAPI::IFile* file); + +public: + virtual void WriteZoneDef() = 0; +}; + +class IZoneDefWriter +{ +public: + virtual bool CanHandleZone(Zone* zone) const = 0; + virtual void WriteZoneDef(Zone* zone, FileAPI::IFile* file) const = 0; +}; \ No newline at end of file diff --git a/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp b/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp new file mode 100644 index 00000000..55c68643 --- /dev/null +++ b/src/Unlinker/Game/T6/ZoneDefWriterT6.cpp @@ -0,0 +1,91 @@ +#include "ZoneDefWriterT6.h" +#include "Game/T6/GameT6.h" +#include "Game/T6/CommonT6.h" +#include "Game/T6/GameAssetPoolT6.h" + +#include +#include + +using namespace T6; + +class ZoneDefWriterT6Impl final : public AbstractZoneDefWriter +{ + 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); + } + +public: + ZoneDefWriterT6Impl(Zone* zone, FileAPI::IFile* file) + : AbstractZoneDefWriter(zone, file) + { + } + + void WriteZoneDef() override + { + WriteComment("Call Of Duty: Black Ops II"); + WriteMetaData(META_DATA_KEY_GAME, "t6"); + EmptyLine(); + + auto* assetPoolT6 = dynamic_cast(m_zone->GetPools()); + + 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(); + } +}; + +bool ZoneDefWriterT6::CanHandleZone(Zone* zone) const +{ + return zone->m_game == &g_GameT6; +} + +void ZoneDefWriterT6::WriteZoneDef(Zone* zone, FileAPI::IFile* file) const +{ + ZoneDefWriterT6Impl writer(zone, file); + writer.WriteZoneDef(); +} diff --git a/src/Unlinker/Game/T6/ZoneDefWriterT6.h b/src/Unlinker/Game/T6/ZoneDefWriterT6.h new file mode 100644 index 00000000..59a82b5b --- /dev/null +++ b/src/Unlinker/Game/T6/ZoneDefWriterT6.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ContentLister/ZoneDefWriter.h" + +class ZoneDefWriterT6 final : public IZoneDefWriter +{ +public: + bool CanHandleZone(Zone* zone) const override; + void WriteZoneDef(Zone* zone, FileAPI::IFile* file) const override; +}; \ No newline at end of file diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index 5d754124..1e9c253a 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -3,12 +3,14 @@ #include "Utils/Arguments/ArgumentParser.h" #include "ZoneLoading.h" #include "ObjWriting.h" -#include "ContentPrinter.h" +#include "ContentLister/ContentPrinter.h" #include "Utils/PathUtils.h" #include "Utils/FileAPI.h" #include "ObjLoading.h" #include "SearchPath/SearchPaths.h" #include "SearchPath/SearchPathFilesystem.h" +#include "ContentLister/ZoneDefWriter.h" +#include "Game/T6/ZoneDefWriterT6.h" #include #include @@ -16,6 +18,11 @@ #include "ObjContainer/IWD/IWD.h" #include "UnlinkerArgs.h" +const IZoneDefWriter* const ZONE_DEF_WRITERS[] +{ + new ZoneDefWriterT6() +}; + class Unlinker::Impl { UnlinkerArgs m_args; @@ -158,7 +165,14 @@ class Unlinker::Impl if (zoneDefinitionFile.IsOpen()) { - ObjWriting::WriteZoneDefinition(zone, &zoneDefinitionFile); + for (auto zoneDefWriter : ZONE_DEF_WRITERS) + { + if (zoneDefWriter->CanHandleZone(zone)) + { + zoneDefWriter->WriteZoneDef(zone, &zoneDefinitionFile); + break; + } + } ObjWriting::DumpZone(zone, outputFolderPath); } else diff --git a/src/Unlinker/UnlinkerArgs.cpp b/src/Unlinker/UnlinkerArgs.cpp index e4da4e63..6091ab27 100644 --- a/src/Unlinker/UnlinkerArgs.cpp +++ b/src/Unlinker/UnlinkerArgs.cpp @@ -206,7 +206,7 @@ bool UnlinkerArgs::ParseArgs(const int argc, const char** argv) SetVerbose(m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)); // -min; --minimal-zone - ObjWriting::Configuration.MinimalZoneFileOutput = m_argument_parser.IsOptionSpecified(OPTION_MINIMAL_ZONE_FILE); + m_minimal_zone_def = m_argument_parser.IsOptionSpecified(OPTION_MINIMAL_ZONE_FILE); // -l; --list if (m_argument_parser.IsOptionSpecified(OPTION_LIST)) diff --git a/src/Unlinker/UnlinkerArgs.h b/src/Unlinker/UnlinkerArgs.h index fc4e91e4..6e6a17ac 100644 --- a/src/Unlinker/UnlinkerArgs.h +++ b/src/Unlinker/UnlinkerArgs.h @@ -37,6 +37,7 @@ public: ProcessingTask m_task; std::string m_output_folder; + bool m_minimal_zone_def; bool m_verbose; diff --git a/src/ZoneCommon/Game/T6/CommonT6.cpp b/src/ZoneCommon/Game/T6/CommonT6.cpp new file mode 100644 index 00000000..5a57cfcd --- /dev/null +++ b/src/ZoneCommon/Game/T6/CommonT6.cpp @@ -0,0 +1,18 @@ +#include "CommonT6.h" + +int CommonT6::Com_HashKey(const char* str, const int maxLen) +{ + if (str == nullptr) + return 0; + + int hash = 0; + for (int i = 0; i < maxLen; i++) + { + if (str[i] == '\0') + break; + + hash += str[i] * (0x77 + i); + } + + return hash ^ ((hash ^ (hash >> 10)) >> 10); +} \ No newline at end of file diff --git a/src/ZoneCommon/Game/T6/CommonT6.h b/src/ZoneCommon/Game/T6/CommonT6.h new file mode 100644 index 00000000..c80b4d5f --- /dev/null +++ b/src/ZoneCommon/Game/T6/CommonT6.h @@ -0,0 +1,7 @@ +#pragma once + +class CommonT6 +{ +public: + static int Com_HashKey(const char* str, int maxLen); +}; \ No newline at end of file