diff --git a/src/ObjCommon.lua b/src/ObjCommon.lua index 51b0dc48..93dccfea 100644 --- a/src/ObjCommon.lua +++ b/src/ObjCommon.lua @@ -2,6 +2,7 @@ ObjCommon = {} function ObjCommon:include() ZoneCommon:include() + minizip:include() includedirs { path.join(ProjectFolder(), "ObjCommon") } @@ -10,6 +11,7 @@ end function ObjCommon:link() Utils:link() ZoneCommon:link() + minizip:link() links { "ObjCommon" } diff --git a/src/ObjCommon/ObjContainer/IObjContainer.h b/src/ObjCommon/ObjContainer/IObjContainer.h index 30342a3e..1c4aa6a5 100644 --- a/src/ObjCommon/ObjContainer/IObjContainer.h +++ b/src/ObjCommon/ObjContainer/IObjContainer.h @@ -7,5 +7,5 @@ class IObjContainer public: virtual ~IObjContainer() = default; - virtual const std::string& GetName() = 0; + virtual std::string GetName() = 0; }; \ No newline at end of file diff --git a/src/ObjCommon/Utils/ZlibFileWrapper.cpp b/src/ObjCommon/Utils/ZlibFileWrapper.cpp new file mode 100644 index 00000000..0386d114 --- /dev/null +++ b/src/ObjCommon/Utils/ZlibFileWrapper.cpp @@ -0,0 +1,87 @@ +#include "ZlibFileWrapper.h" +#include + +voidpf Wrapper_Zlib_FileOpen(voidpf opaque, const char*, int) +{ + return opaque; +} + +uLong Wrapper_Zlib_FileRead(voidpf, voidpf stream, void* buf, const uLong size) +{ + auto* file = reinterpret_cast(stream); + + return file->Read(buf, 1, size); +} + +uLong Wrapper_Zlib_FileWrite(voidpf opaque, voidpf stream, const void* buf, const uLong size) +{ + auto* file = reinterpret_cast(stream); + + return file->Write(buf, 1, size); +} + +long Wrapper_Zlib_FileTell(voidpf opaque, voidpf stream) +{ + auto* file = reinterpret_cast(stream); + + return static_cast(file->Pos()); +} + +long Wrapper_Zlib_FileSeek(voidpf opaque, voidpf stream, const uLong offset, const int origin) +{ + auto* file = reinterpret_cast(stream); + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + file->Skip(offset); + break; + + case ZLIB_FILEFUNC_SEEK_END: + assert(offset == 0); + file->GotoEnd(); + break; + + case ZLIB_FILEFUNC_SEEK_SET: + file->Goto(offset); + break; + + default: + return -1; + } + + return 0; +} + +int Wrapper_Zlib_FileClose(voidpf opaque, voidpf stream) +{ + auto* file = reinterpret_cast(stream); + + if (file->IsOpen()) + { + file->Close(); + return 0; + } + + return -1; +} + +int Wrapper_Zlib_FileError(voidpf opaque, voidpf stream) +{ + return 0; +} + +zlib_filefunc_def ZlibFileWrapper::CreateFunctions32ForFile(FileAPI::IFile* file) +{ + return zlib_filefunc_def_s + { + Wrapper_Zlib_FileOpen, + Wrapper_Zlib_FileRead, + Wrapper_Zlib_FileWrite, + Wrapper_Zlib_FileTell, + Wrapper_Zlib_FileSeek, + Wrapper_Zlib_FileClose, + Wrapper_Zlib_FileError, + file + }; +} diff --git a/src/ObjCommon/Utils/ZlibFileWrapper.h b/src/ObjCommon/Utils/ZlibFileWrapper.h new file mode 100644 index 00000000..e059bf5d --- /dev/null +++ b/src/ObjCommon/Utils/ZlibFileWrapper.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "Utils/FileAPI.h" + +class ZlibFileWrapper +{ +public: + static zlib_filefunc_def CreateFunctions32ForFile(FileAPI::IFile* file); +}; diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 218c8d49..5014b16a 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -1,9 +1,10 @@ #include "ObjLoaderT6.h" #include "Game/T6/GameT6.h" #include "Game/T6/GameAssetPoolT6.h" +#include "ObjContainer/IPak/IPak.h" -const int ObjLoaderT6::IPAK_READ_HASH = ObjLoaderT6::Com_HashKey("ipak_read", 64); -const int ObjLoaderT6::GLOBAL_HASH = ObjLoaderT6::Com_HashKey("GLOBAL", 64); +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) { @@ -27,10 +28,18 @@ bool ObjLoaderT6::SupportsZone(Zone* zone) return zone->m_game == &g_GameT6; } -void ObjLoaderT6::LoadIPakForZone(const std::string& ipakName, Zone* zone) +void ObjLoaderT6::LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone) { printf("Loading ipak '%s' for zone '%s'\n", ipakName.c_str(), zone->m_name.c_str()); - // TODO + + const std::string ipakFilename = ipakName + ".ipak"; + + auto* file = searchPath->Open(ipakFilename); + + if(file && file->IsOpen()) + { + + } } void ObjLoaderT6::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) @@ -48,13 +57,18 @@ void ObjLoaderT6::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* if(variable->namespaceHash == zoneNameHash && variable->keyHash == IPAK_READ_HASH) { - LoadIPakForZone(variable->value, zone); + LoadIPakForZone(searchPath, variable->value, zone); } } } } } +void ObjLoaderT6::UnloadContainersOfZone(Zone* zone) +{ + IPak::Repository.RemoveContainerReferences(zone); +} + void ObjLoaderT6::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) { // TODO diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.h b/src/ObjLoading/Game/T6/ObjLoaderT6.h index dac1af4f..0ff86dc1 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.h +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.h @@ -1,6 +1,7 @@ #pragma once #include "IObjLoader.h" +#include "SearchPath/ISearchPath.h" class ObjLoaderT6 final : public IObjLoader { @@ -8,10 +9,13 @@ class ObjLoaderT6 final : public IObjLoader static const int GLOBAL_HASH; static int Com_HashKey(const char* str, int maxLen); - static void LoadIPakForZone(const std::string& ipakName, Zone* zone); + static void LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone); public: bool SupportsZone(Zone* zone) override; + void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) override; + void UnloadContainersOfZone(Zone* zone) override; + void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) override; }; diff --git a/src/ObjLoading/IObjLoader.h b/src/ObjLoading/IObjLoader.h index 3a87267b..90cee1ac 100644 --- a/src/ObjLoading/IObjLoader.h +++ b/src/ObjLoading/IObjLoader.h @@ -9,6 +9,9 @@ public: virtual ~IObjLoader() = default; virtual bool SupportsZone(Zone* zone) = 0; + virtual void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) = 0; + virtual void UnloadContainersOfZone(Zone* zone) = 0; + virtual void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) = 0; }; \ No newline at end of file diff --git a/src/ObjLoading/ObjContainer/IPak/IPak.cpp b/src/ObjLoading/ObjContainer/IPak/IPak.cpp index 6312616c..346c0742 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPak.cpp +++ b/src/ObjLoading/ObjContainer/IPak/IPak.cpp @@ -3,10 +3,13 @@ #include "Exception/IPakLoadException.h" #include +#include "Utils/PathUtils.h" const uint32_t IPak::MAGIC = 'IPAK'; const uint32_t IPak::VERSION = 0x50000; +ObjContainerRepository IPak::Repository; + uint32_t IPak::R_HashString(const char* str, uint32_t hash) { for (const char* pos = str; *pos; pos++) @@ -17,8 +20,9 @@ uint32_t IPak::R_HashString(const char* str, uint32_t hash) return hash; } -IPak::IPak(FileAPI::IFile* file) +IPak::IPak(std::string path, FileAPI::IFile* file) { + m_path = std::move(path); m_file = file; m_initialized = false; m_index_section = nullptr; @@ -34,6 +38,11 @@ IPak::~IPak() m_data_section = nullptr; } +std::string IPak::GetName() +{ + return utils::Path::GetFilename(m_path); +} + void IPak::ReadSection() { IPakSection section{}; diff --git a/src/ObjLoading/ObjContainer/IPak/IPak.h b/src/ObjLoading/ObjContainer/IPak/IPak.h index 6ee12147..ec91d137 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPak.h +++ b/src/ObjLoading/ObjContainer/IPak/IPak.h @@ -3,6 +3,8 @@ #include "Utils/FileAPI.h" #include "ObjContainer/IPak/IPakTypes.h" #include "ObjContainer/ObjContainerReferenceable.h" +#include "ObjContainer/ObjContainerRepository.h" +#include "Zone/Zone.h" #include @@ -11,6 +13,7 @@ class IPak final : public ObjContainerReferenceable static const uint32_t MAGIC; static const uint32_t VERSION; + std::string m_path; FileAPI::IFile* m_file; bool m_initialized; @@ -26,9 +29,13 @@ class IPak final : public ObjContainerReferenceable void ReadHeader(); public: - explicit IPak(FileAPI::IFile* file); + static ObjContainerRepository Repository; + + IPak(std::string path, FileAPI::IFile* file); ~IPak(); + std::string GetName() override; + void Initialize(); FileAPI::IFile* GetEntryData(IPakHash nameHash, IPakHash dataHash); diff --git a/src/ObjLoading/ObjContainer/IWD/IWD.cpp b/src/ObjLoading/ObjContainer/IWD/IWD.cpp index c07f2a42..09c8cafd 100644 --- a/src/ObjLoading/ObjContainer/IWD/IWD.cpp +++ b/src/ObjLoading/ObjContainer/IWD/IWD.cpp @@ -1,32 +1,166 @@ #include "IWD.h" -IWD::IWD(std::string path) +#include +#include +#include "Utils/PathUtils.h" +#include "Utils/ZlibFileWrapper.h" +#include +#include "ObjLoading.h" + +ObjContainerRepository IWD::Repository; + +class IWD::Impl : public ISearchPath, IObjContainer { - m_path = std::move(path); + class IWDEntry + { + public: + std::string m_name; + unz_file_pos m_file_pos; + }; + + std::string m_path; + FileAPI::IFile* m_file; + unzFile m_unz_file; + + std::vector m_entries; + +public: + Impl(std::string path, FileAPI::IFile* file) + { + m_unz_file = nullptr; + m_path = std::move(path); + m_file = file; + } + + ~Impl() + { + if (m_unz_file != nullptr) + { + unzClose(m_unz_file); + } + + if (m_file) + { + m_file->Close(); + delete m_file; + m_file = nullptr; + } + } + + bool Initialize() + { + auto ioFunctions = ZlibFileWrapper::CreateFunctions32ForFile(m_file); + m_unz_file = unzOpen2("", &ioFunctions); + + if (m_unz_file == nullptr) + { + printf("Could not open IWD \"%s\"\n", m_path.c_str()); + return false; + } + + auto ret = unzGoToFirstFile(m_unz_file); + while (ret == Z_OK) + { + unz_file_info info; + char fileNameBuffer[256]; + unzGetCurrentFileInfo(m_unz_file, &info, fileNameBuffer, sizeof fileNameBuffer, nullptr, 0, nullptr, 0); + + std::string fileName(fileNameBuffer); + std::filesystem::path path(fileName); + + if (path.has_filename()) + { + IWDEntry entry; + entry.m_name = std::move(fileName); + unzGetFilePos(m_unz_file, &entry.m_file_pos); + m_entries.push_back(entry); + } + + ret = unzGoToNextFile(m_unz_file); + } + + if(ObjLoading::Configuration.Verbose) + { + printf("Loaded IWD \"%s\" with %u entries\n", m_path.c_str(), m_entries.size()); + } + + return true; + } + + FileAPI::IFile* Open(const std::string& fileName) override + { + if (m_unz_file == nullptr) + { + return nullptr; + } + + // TODO + + return nullptr; + } + + std::string GetPath() override + { + return m_path; + } + + std::string GetName() override + { + return utils::Path::GetFilename(m_path); + } + + void Find(const SearchPathSearchOptions& options, const std::function& callback) override + { + // TODO + } +}; + +IWD::IWD(std::string path, FileAPI::IFile* file) +{ + m_impl = new Impl(std::move(path), file); +} + +IWD::~IWD() +{ + delete m_impl; + m_impl = nullptr; +} + +IWD::IWD(IWD&& other) noexcept +{ + m_impl = other.m_impl; + other.m_impl = nullptr; +} + +IWD& IWD::operator=(IWD&& other) noexcept +{ + m_impl = other.m_impl; + other.m_impl = nullptr; + + return *this; +} + +bool IWD::Initialize() const +{ + return m_impl->Initialize(); } FileAPI::IFile* IWD::Open(const std::string& fileName) { - // TODO - return nullptr; + return m_impl->Open(fileName); } -void IWD::FindAll(std::function callback) +std::string IWD::GetPath() { - // TODO + return m_impl->GetPath(); } -void IWD::FindAllOnDisk(std::function callback) +std::string IWD::GetName() { - // Files inside an IWD are not on the disk's file system directly. Therefore do nothing here. + return m_impl->GetName(); } -void IWD::FindByExtension(const std::string& extension, std::function callback) +void IWD::Find(const SearchPathSearchOptions& options, const std::function& callback) { - // TODO -} - -void IWD::FindOnDiskByExtension(const std::string& extension, std::function callback) -{ - // Files inside an IWD are not on the disk's file system directly. Therefore do nothing here. + return m_impl->Find(options, callback); } diff --git a/src/ObjLoading/ObjContainer/IWD/IWD.h b/src/ObjLoading/ObjContainer/IWD/IWD.h index 257f5e1f..3e2e8b3c 100644 --- a/src/ObjLoading/ObjContainer/IWD/IWD.h +++ b/src/ObjLoading/ObjContainer/IWD/IWD.h @@ -1,17 +1,28 @@ #pragma once #include "SearchPath/ISearchPath.h" +#include "ObjContainer/ObjContainerRepository.h" -class IWD final : public ISearchPath +class IWD final : public ISearchPath, IObjContainer { - std::string m_path; + class Impl; + Impl* m_impl; public: - explicit IWD(std::string path); + static ObjContainerRepository Repository; + + IWD(std::string path, FileAPI::IFile* file); + ~IWD(); + + IWD(const IWD& other) = delete; + IWD(IWD&& other) noexcept; + IWD& operator=(const IWD& other) = delete; + IWD& operator=(IWD&& other) noexcept; + + bool Initialize() const; FileAPI::IFile* Open(const std::string& fileName) override; - void FindAll(std::function callback) override; - void FindAllOnDisk(std::function callback) override; - void FindByExtension(const std::string& extension, std::function callback) override; - void FindOnDiskByExtension(const std::string& extension, std::function callback) override; + std::string GetPath() override; + std::string GetName() override; + void Find(const SearchPathSearchOptions& options, const std::function& callback) override; }; \ No newline at end of file diff --git a/src/ObjLoading/ObjContainer/ObjContainerRegistry.cpp b/src/ObjLoading/ObjContainer/ObjContainerRegistry.cpp deleted file mode 100644 index afed9b71..00000000 --- a/src/ObjLoading/ObjContainer/ObjContainerRegistry.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "ObjContainerRegistry.h" -#include - -ObjContainerRegistry g_ObjContainerRegistry; - -IObjContainer* ObjContainerRegistry::GetContainerByName(const std::string& name) -{ - for (auto* container : m_containers) - { - if (container->GetName() == name) - { - return container; - } - } - - return nullptr; -} - -void ObjContainerRegistry::AddContainer(IObjContainer* container) -{ - assert(dynamic_cast(container) == nullptr); - - m_containers.push_back(container); -} - -void ObjContainerRegistry::AddContainerWithReference(ObjContainerReferenceable* container, Zone* referencer) -{ - container->AddReference(referencer); - m_containers.push_back(container); -} - -void ObjContainerRegistry::RemoveContainerReferences(Zone* referencer) -{ - auto iContainer = m_containers.begin(); - - while (iContainer != m_containers.end()) - { - auto* container = *iContainer; - - if (auto* referenceableContainer = dynamic_cast(container)) - { - if (referenceableContainer->RemoveReference(referencer) && !referenceableContainer->IsReferenced()) - { - delete container; - iContainer = m_containers.erase(iContainer); - continue; - } - } - - ++iContainer; - } -} diff --git a/src/ObjLoading/ObjContainer/ObjContainerRegistry.h b/src/ObjLoading/ObjContainer/ObjContainerRegistry.h deleted file mode 100644 index 3acfb715..00000000 --- a/src/ObjLoading/ObjContainer/ObjContainerRegistry.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "ObjContainer/IObjContainer.h" -#include "ObjContainer/ObjContainerReferenceable.h" -#include "Zone/Zone.h" - -#include -#include - -class ObjContainerRegistry -{ - std::vector m_containers; - -public: - void AddContainer(IObjContainer* container); - void AddContainerWithReference(ObjContainerReferenceable* container, Zone* referencer); - void RemoveContainerReferences(Zone* referencer); - - IObjContainer* GetContainerByName(const std::string& name); -}; - -extern ObjContainerRegistry g_ObjContainerRegistry; \ No newline at end of file diff --git a/src/ObjLoading/ObjContainer/ObjContainerRepository.h b/src/ObjLoading/ObjContainer/ObjContainerRepository.h new file mode 100644 index 00000000..a5c903d8 --- /dev/null +++ b/src/ObjLoading/ObjContainer/ObjContainerRepository.h @@ -0,0 +1,93 @@ +#pragma once + +#include "ObjContainer/IObjContainer.h" +#include "Utils/TransformIterator.h" + +#include +#include +#include +#include + +template +class ObjContainerRepository +{ + class ObjContainerEntry + { + public: + ContainerType* m_container; + std::set m_references; + + explicit ObjContainerEntry(ContainerType* container) + { + m_container = container; + } + }; + + std::vector m_containers; + +public: + void AddContainer(ContainerType* container, ReferencerType* referencer) + { + auto firstEntry = std::find_if(m_containers.begin(), m_containers.end(), + [container](ObjContainerEntry& entry) -> bool + { + return entry.m_container == container; + }); + + if(firstEntry != m_containers.end()) + { + firstEntry->m_references.insert(referencer); + return; + } + + ObjContainerEntry entry(container); + entry.m_references.insert(referencer); + m_containers.push_back(entry); + } + + void RemoveContainerReferences(ReferencerType* referencer) + { + for(auto iEntry = m_containers.begin(); iEntry != m_containers.end(); ++iEntry) + { + auto foundReference = iEntry->m_references.find(referencer); + + if(foundReference != iEntry->m_references.end()) + { + iEntry->m_references.erase(foundReference); + } + + if(iEntry->m_references.empty()) + { + delete iEntry->m_container; + m_containers.erase(iEntry); + } + } + } + + IObjContainer* GetContainerByName(const std::string& name) + { + auto foundEntry = std::find_if(m_containers.begin(), m_containers.end(), [name](ObjContainerEntry& entry) -> bool + { + return entry.m_container->GetName() == name; + }); + + if(foundEntry != m_containers.end()) + { + return foundEntry->m_container; + } + + return nullptr; + } + + TransformIterator::iterator, ObjContainerEntry&, ContainerType*> begin() + { + return TransformIterator::iterator, ObjContainerEntry&, ContainerType*>( + m_containers.begin(), [](ObjContainerEntry& entry) -> ContainerType* { return entry.m_container; }); + } + + TransformIterator::iterator, ObjContainerEntry&, ContainerType*> end() + { + return TransformIterator::iterator, ObjContainerEntry&, ContainerType*>( + m_containers.end(), [](ObjContainerEntry& entry) -> ContainerType* { return entry.m_container; }); + } +}; diff --git a/src/ObjLoading/ObjLoading.cpp b/src/ObjLoading/ObjLoading.cpp index bd8e71eb..6912d314 100644 --- a/src/ObjLoading/ObjLoading.cpp +++ b/src/ObjLoading/ObjLoading.cpp @@ -1,13 +1,18 @@ #include "ObjLoading.h" #include "IObjLoader.h" #include "Game/T6/ObjLoaderT6.h" -#include "ObjContainer/ObjContainerRegistry.h" +#include "ObjContainer/IWD/IWD.h" +#include "SearchPath/SearchPaths.h" + +ObjLoading::Configuration_t ObjLoading::Configuration; IObjLoader* objLoaders[] { new ObjLoaderT6() }; +SearchPaths iwdSearchPaths; + void ObjLoading::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) { for (auto* loader : objLoaders) @@ -34,5 +39,49 @@ void ObjLoading::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) void ObjLoading::UnloadContainersOfZone(Zone* zone) { - g_ObjContainerRegistry.RemoveContainerReferences(zone); + for (auto* loader : objLoaders) + { + if (loader->SupportsZone(zone)) + { + loader->UnloadContainersOfZone(zone); + return; + } + } +} + +void ObjLoading::LoadIWDsInSearchPath(ISearchPath* searchPath) +{ + searchPath->Find(SearchPathSearchOptions().IncludeSubdirectories(false).FilterExtensions("iwd"), + [searchPath](const std::string& path) -> void + { + auto file = FileAPI::Open(path, FileAPI::Mode::MODE_READ); + + if (file.IsOpen()) + { + const auto fileP = new FileAPI::File(std::move(file)); + IWD* iwd = new IWD(path, fileP); + + if (iwd->Initialize()) + { + IWD::Repository.AddContainer(iwd, searchPath); + } + else + { + delete iwd; + + fileP->Close(); + delete fileP; + } + } + }); +} + +void ObjLoading::UnloadIWDsInSearchPath(ISearchPath* searchPath) +{ + IWD::Repository.RemoveContainerReferences(searchPath); +} + +ISearchPath* ObjLoading::GetIWDSearchPaths() +{ + return &iwdSearchPaths; } diff --git a/src/ObjLoading/ObjLoading.h b/src/ObjLoading/ObjLoading.h index 6f183546..d618537b 100644 --- a/src/ObjLoading/ObjLoading.h +++ b/src/ObjLoading/ObjLoading.h @@ -6,8 +6,18 @@ class ObjLoading { public: + static class Configuration_t + { + public: + bool Verbose = false; + } Configuration; + static void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone); static void UnloadContainersOfZone(Zone* zone); + static void LoadIWDsInSearchPath(ISearchPath* searchPath); + static void UnloadIWDsInSearchPath(ISearchPath* searchPath); + static ISearchPath* GetIWDSearchPaths(); + static void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone); -}; \ No newline at end of file +}; diff --git a/src/ObjLoading/SearchPath/ISearchPath.h b/src/ObjLoading/SearchPath/ISearchPath.h index f5308a55..fb559e81 100644 --- a/src/ObjLoading/SearchPath/ISearchPath.h +++ b/src/ObjLoading/SearchPath/ISearchPath.h @@ -1,7 +1,7 @@ #pragma once +#include "SearchPathSearchOptions.h" #include "Utils/FileAPI.h" -#include #include class ISearchPath @@ -16,29 +16,25 @@ public: */ virtual FileAPI::IFile* Open(const std::string& fileName) = 0; + /** + * \brief Returns the path to the search path. + * \return The path to the search path. + */ + virtual std::string GetPath() = 0; + + /** + * \brief Iterates through all files of the search path. + * \param callback The callback to call for each found file with it's path relative to the search path. + * \param options Options that modify the search. + */ + virtual void Find(const SearchPathSearchOptions& options, const std::function& callback) = 0; + /** * \brief Iterates through all files of the search path. * \param callback The callback to call for each found file with it's path relative to the search path. */ - virtual void FindAll(std::function callback) = 0; - - /** - * \brief Iterates through all files available through the OS file system. - * \param callback The callback to call for each found file with it's full path. - */ - virtual void FindAllOnDisk(std::function callback) = 0; - - /** - * \brief Iterates through all files of the search path with the specified extension. - * \param extension The extension of all files to find. - * \param callback The callback to call for each found file with it's path relative to the search path. - */ - virtual void FindByExtension(const std::string& extension, std::function callback) = 0; - - /** - * \brief Iterates through all files available through the OS file system with the specified extension. - * \param extension The extension of all files to find. - * \param callback The callback to call for each found file with it's full path. - */ - virtual void FindOnDiskByExtension(const std::string& extension, std::function callback) = 0; + void Find(const std::function& callback) + { + Find(SearchPathSearchOptions(), callback); + } }; diff --git a/src/ObjLoading/SearchPath/SearchPathFilesystem.cpp b/src/ObjLoading/SearchPath/SearchPathFilesystem.cpp index 1282b3f7..b95f5a5b 100644 --- a/src/ObjLoading/SearchPath/SearchPathFilesystem.cpp +++ b/src/ObjLoading/SearchPath/SearchPathFilesystem.cpp @@ -8,7 +8,7 @@ SearchPathFilesystem::SearchPathFilesystem(std::string path) m_path = std::move(path); } -const std::string& SearchPathFilesystem::GetPath() const +std::string SearchPathFilesystem::GetPath() { return m_path; } @@ -25,55 +25,28 @@ FileAPI::IFile* SearchPathFilesystem::Open(const std::string& fileName) return nullptr; } - -void SearchPathFilesystem::FindAll(const std::function callback) +void SearchPathFilesystem::Find(const SearchPathSearchOptions& options, const std::function& callback) { - std::filesystem::recursive_directory_iterator iterator(m_path); - - for (const auto entry = begin(iterator); iterator != end(iterator); ++iterator) + if (options.m_should_include_subdirectories) { - callback(entry->path().string()); - } -} - -void SearchPathFilesystem::FindAllOnDisk(const std::function callback) -{ - std::filesystem::recursive_directory_iterator iterator(m_path); - - for (const auto entry = begin(iterator); iterator != end(iterator); ++iterator) - { - callback(absolute(entry->path()).string()); - } -} - -void SearchPathFilesystem::FindByExtension(const std::string& extension, - const std::function callback) -{ - std::filesystem::recursive_directory_iterator iterator(m_path); - - for (const auto entry = begin(iterator); iterator != end(iterator); ++iterator) - { - auto entryPath = entry->path(); - - if (entryPath.extension().string() == extension) + std::filesystem::recursive_directory_iterator iterator(m_path); + for (const auto entry = begin(iterator); iterator != end(iterator); ++iterator) { - callback(entryPath.string()); - } - } -} - -void SearchPathFilesystem::FindOnDiskByExtension(const std::string& extension, - const std::function callback) -{ - std::filesystem::recursive_directory_iterator iterator(m_path); - - for (const auto entry = begin(iterator); iterator != end(iterator); ++iterator) - { - auto entryPath = entry->path(); - - if (entryPath.extension().string() == extension) - { - callback(absolute(entryPath).string()); + auto path = entry->path(); + if (options.m_filter_extensions && path.extension().string() != options.m_extension) + continue; + callback(options.m_absolute_paths ? absolute(path).string() : path.string()); + } + } + else + { + std::filesystem::directory_iterator iterator(m_path); + for (const auto entry = begin(iterator); iterator != end(iterator); ++iterator) + { + auto path = entry->path(); + if (options.m_filter_extensions && path.extension().string() != options.m_extension) + continue; + callback(options.m_absolute_paths ? absolute(path).string() : path.string()); } } } diff --git a/src/ObjLoading/SearchPath/SearchPathFilesystem.h b/src/ObjLoading/SearchPath/SearchPathFilesystem.h index 2d605650..82e827a1 100644 --- a/src/ObjLoading/SearchPath/SearchPathFilesystem.h +++ b/src/ObjLoading/SearchPath/SearchPathFilesystem.h @@ -9,11 +9,8 @@ class SearchPathFilesystem final : public ISearchPath public: explicit SearchPathFilesystem(std::string path); - const std::string& GetPath() const; FileAPI::IFile* Open(const std::string& fileName) override; - void FindAll(std::function callback) override; - void FindAllOnDisk(std::function callback) override; - void FindByExtension(const std::string& extension, std::function callback) override; - void FindOnDiskByExtension(const std::string& extension, std::function callback) override; + std::string GetPath() override; + void Find(const SearchPathSearchOptions& options, const std::function& callback) override; }; \ No newline at end of file diff --git a/src/ObjLoading/SearchPath/SearchPathSearchOptions.cpp b/src/ObjLoading/SearchPath/SearchPathSearchOptions.cpp new file mode 100644 index 00000000..bf943b41 --- /dev/null +++ b/src/ObjLoading/SearchPath/SearchPathSearchOptions.cpp @@ -0,0 +1,41 @@ +#include "SearchPathSearchOptions.h" + +SearchPathSearchOptions::SearchPathSearchOptions() +{ + m_should_include_subdirectories = true; + m_disk_files_only = false; + m_absolute_paths = false; + m_filter_extensions = false; +} + +SearchPathSearchOptions& SearchPathSearchOptions::IncludeSubdirectories(const bool value) +{ + m_should_include_subdirectories = value; + + return *this; +} + +SearchPathSearchOptions& SearchPathSearchOptions::OnlyDiskFiles(const bool value) +{ + m_disk_files_only = value; + + return *this; +} + +SearchPathSearchOptions& SearchPathSearchOptions::AbsolutePaths(const bool value) +{ + m_absolute_paths = value; + + return *this; +} + +SearchPathSearchOptions& SearchPathSearchOptions::FilterExtensions(std::string extension) +{ + m_extension = std::move(extension); + m_filter_extensions = true; + + if(m_extension[0] != '.') + m_extension = "." + m_extension; + + return *this; +} diff --git a/src/ObjLoading/SearchPath/SearchPathSearchOptions.h b/src/ObjLoading/SearchPath/SearchPathSearchOptions.h new file mode 100644 index 00000000..d34cd9f7 --- /dev/null +++ b/src/ObjLoading/SearchPath/SearchPathSearchOptions.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +class SearchPathSearchOptions +{ +public: + bool m_should_include_subdirectories; + bool m_disk_files_only; + bool m_absolute_paths; + + bool m_filter_extensions; + std::string m_extension; + + SearchPathSearchOptions(); + + SearchPathSearchOptions& IncludeSubdirectories(bool value); + SearchPathSearchOptions& OnlyDiskFiles(bool value); + SearchPathSearchOptions& AbsolutePaths(bool value); + SearchPathSearchOptions& FilterExtensions(std::string extension); +}; \ No newline at end of file diff --git a/src/ObjLoading/SearchPath/SearchPaths.cpp b/src/ObjLoading/SearchPath/SearchPaths.cpp index 3a2ea305..b28346b2 100644 --- a/src/ObjLoading/SearchPath/SearchPaths.cpp +++ b/src/ObjLoading/SearchPath/SearchPaths.cpp @@ -56,36 +56,16 @@ FileAPI::IFile* SearchPaths::Open(const std::string& fileName) return nullptr; } - -void SearchPaths::FindAll(const std::function callback) +std::string SearchPaths::GetPath() { - for (auto searchPathEntry : m_search_paths) - { - searchPathEntry->FindAll(callback); - } + return "SearchPaths: " + std::to_string(m_search_paths.size()) + " entries"; } -void SearchPaths::FindAllOnDisk(const std::function callback) +void SearchPaths::Find(const SearchPathSearchOptions& options, const std::function& callback) { for (auto searchPathEntry : m_search_paths) { - searchPathEntry->FindAllOnDisk(callback); - } -} - -void SearchPaths::FindByExtension(const std::string& extension, const std::function callback) -{ - for (auto searchPathEntry : m_search_paths) - { - searchPathEntry->FindByExtension(extension, callback); - } -} - -void SearchPaths::FindOnDiskByExtension(const std::string& extension, const std::function callback) -{ - for (auto searchPathEntry : m_search_paths) - { - searchPathEntry->FindOnDiskByExtension(extension, callback); + searchPathEntry->Find(options, callback); } } diff --git a/src/ObjLoading/SearchPath/SearchPaths.h b/src/ObjLoading/SearchPath/SearchPaths.h index 844427e6..918cafc5 100644 --- a/src/ObjLoading/SearchPath/SearchPaths.h +++ b/src/ObjLoading/SearchPath/SearchPaths.h @@ -15,10 +15,8 @@ public: ~SearchPaths() override; FileAPI::IFile* Open(const std::string& fileName) override; - void FindAll(std::function callback) override; - void FindAllOnDisk(std::function callback) override; - void FindByExtension(const std::string& extension, std::function callback) override; - void FindOnDiskByExtension(const std::string& extension, std::function callback) override; + std::string GetPath() override; + void Find(const SearchPathSearchOptions& options, const std::function& callback) override; SearchPaths(const SearchPaths& other); SearchPaths(SearchPaths&& other) noexcept; diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index 8c6b043b..80048369 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -14,6 +14,7 @@ #include #include #include +#include "ObjContainer/IWD/IWD.h" const CommandLineOption* optionHelp = CommandLineOption::Builder::Create() .WithShortName("?") @@ -80,14 +81,16 @@ class Unlinker::Impl * \brief Loads a search path. * \param searchPath The search path to load. */ - void LoadSearchPath(SearchPathFilesystem* searchPath) + void LoadSearchPath(ISearchPath* searchPath) const { - if(m_should_load_obj) + if (m_should_load_obj) { - if(m_verbose) + if (m_verbose) { printf("Loading search path: \"%s\"\n", searchPath->GetPath().c_str()); } + + ObjLoading::LoadIWDsInSearchPath(searchPath); } } @@ -95,14 +98,16 @@ class Unlinker::Impl * \brief Unloads a search path. * \param searchPath The search path to unload. */ - void UnloadSearchPath(SearchPathFilesystem* searchPath) + void UnloadSearchPath(ISearchPath* searchPath) const { - if(m_should_load_obj) + if (m_should_load_obj) { - if(m_verbose) + if (m_verbose) { printf("Unloading search path: \"%s\"\n", searchPath->GetPath().c_str()); } + + ObjLoading::UnloadIWDsInSearchPath(searchPath); } } @@ -220,6 +225,12 @@ class Unlinker::Impl { std::string absolutePath = std::filesystem::absolute(path).string(); + if (!FileAPI::DirectoryExists(absolutePath)) + { + printf("Could not find directory of search path: \"%s\"\n", path.c_str()); + return false; + } + SearchPathFilesystem* searchPath = new SearchPathFilesystem(absolutePath); LoadSearchPath(searchPath); m_search_paths.CommitSearchPath(searchPath); @@ -327,6 +338,12 @@ class Unlinker::Impl return true; } + void SetVerbose(const bool verbose) + { + m_verbose = verbose; + ObjLoading::Configuration.Verbose = verbose; + } + public: Impl() : m_argument_parser(commandLineOptions, _countof(commandLineOptions)) @@ -347,7 +364,7 @@ public: return false; } - m_verbose = m_argument_parser.IsOptionSpecified(optionVerbose); + SetVerbose(m_argument_parser.IsOptionSpecified(optionVerbose)); m_should_load_obj = !m_argument_parser.IsOptionSpecified(optionList); // Check if the user requested help @@ -384,8 +401,8 @@ public: printf("Failed to load zone \"%s\".\n", zonePath.c_str()); return false; } - - if(m_verbose) + + if (m_verbose) { printf("Loaded zone \"%s\"\n", zone->m_name.c_str()); } diff --git a/src/Utils/Utils/FileAPI.cpp b/src/Utils/Utils/FileAPI.cpp index 44c8e506..7704fe3b 100644 --- a/src/Utils/Utils/FileAPI.cpp +++ b/src/Utils/Utils/FileAPI.cpp @@ -27,7 +27,7 @@ bool FileAPI::DirectoryExists(const std::string& directoryName) FileAPI::File FileAPI::Open(const std::string& filename, const Mode mode) { const char* modeStr; - switch(mode) + switch (mode) { default: case Mode::MODE_READ: @@ -38,14 +38,14 @@ FileAPI::File FileAPI::Open(const std::string& filename, const Mode mode) modeStr = "wb"; break; } - - FILE* handle; - if (fopen_s(&handle, filename.c_str(), modeStr) != 0) - { - return File(nullptr); - } - return File(handle); + FILE* handle; + if (fopen_s(&handle, filename.c_str(), modeStr) != 0) + { + return File(nullptr); + } + + return File(handle); } FileAPI::File::File() @@ -55,7 +55,7 @@ FileAPI::File::File() FileAPI::File::File(void* handle) { - this->m_handle = handle; + this->m_handle = handle; } FileAPI::File::File(File&& f) noexcept @@ -66,7 +66,7 @@ FileAPI::File::File(File&& f) noexcept FileAPI::File::~File() { - this->Close(); + this->Close(); } FileAPI::File& FileAPI::File::operator=(File&& f) noexcept @@ -79,43 +79,48 @@ FileAPI::File& FileAPI::File::operator=(File&& f) noexcept bool FileAPI::File::IsOpen() { - return this->m_handle != nullptr; + return this->m_handle != nullptr; } size_t FileAPI::File::Read(void* buffer, const size_t elementSize, const size_t elementCount) { - if (!this->IsOpen()) - return 0; + if (!this->IsOpen()) + return 0; - return fread(buffer, elementSize, elementCount, static_cast(m_handle)); + return fread(buffer, elementSize, elementCount, static_cast(m_handle)); } size_t FileAPI::File::Write(const void* data, const size_t elementSize, const size_t elementCount) { - if (!this->IsOpen()) - return 0; + if (!this->IsOpen()) + return 0; - return fwrite(data, elementSize, elementCount, static_cast(m_handle)); + return fwrite(data, elementSize, elementCount, static_cast(m_handle)); } -void FileAPI::File::Skip(const size_t amount) +void FileAPI::File::Skip(const int64_t amount) { - if(!this->IsOpen()) + if (!this->IsOpen()) return; - fseek(static_cast(m_handle), amount, SEEK_CUR); + _fseeki64(static_cast(m_handle), amount, SEEK_CUR); +} + +void FileAPI::File::GotoEnd() +{ + _fseeki64(static_cast(m_handle), 0, SEEK_END); } size_t FileAPI::File::Printf(const char* fmt, ...) { - va_list ap; + va_list ap; - if (!this->IsOpen()) - return 0; + if (!this->IsOpen()) + return 0; - va_start(ap, fmt); + va_start(ap, fmt); const int result = vfprintf(static_cast(m_handle), fmt, ap); - va_end(ap); + va_end(ap); return result; } @@ -132,9 +137,9 @@ void FileAPI::File::Goto(const int64_t pos) void FileAPI::File::Close() { - if (this->m_handle != nullptr) - { - fclose(static_cast(m_handle)); - this->m_handle = nullptr; - } -} \ No newline at end of file + if (this->m_handle != nullptr) + { + fclose(static_cast(m_handle)); + this->m_handle = nullptr; + } +} diff --git a/src/Utils/Utils/FileAPI.h b/src/Utils/Utils/FileAPI.h index 121c2abb..9ce9a44e 100644 --- a/src/Utils/Utils/FileAPI.h +++ b/src/Utils/Utils/FileAPI.h @@ -20,10 +20,11 @@ public: virtual bool IsOpen() = 0; virtual size_t Read(void* buffer, size_t elementSize, size_t elementCount) = 0; virtual size_t Write(const void* data, size_t elementSize, size_t elementCount) = 0; - virtual void Skip(size_t amount) = 0; + virtual void Skip(int64_t amount) = 0; virtual size_t Printf(const char* fmt, ...) = 0; virtual int64_t Pos() = 0; virtual void Goto(int64_t pos) = 0; + virtual void GotoEnd() = 0; virtual void Close() = 0; }; @@ -44,10 +45,11 @@ public: bool IsOpen() override; size_t Read(void* buffer, size_t elementSize, size_t elementCount) override; size_t Write(const void* data, size_t elementSize, size_t elementCount) override; - void Skip(size_t amount) override; + void Skip(int64_t amount) override; size_t Printf(const char* fmt, ...) override; int64_t Pos() override; void Goto(int64_t pos) override; + void GotoEnd() override; void Close() override; }; diff --git a/src/Utils/Utils/TransformIterator.h b/src/Utils/Utils/TransformIterator.h new file mode 100644 index 00000000..619609af --- /dev/null +++ b/src/Utils/Utils/TransformIterator.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +template +class TransformIterator +{ + BaseIteratorType m_base; + std::function m_transform; + +public: + TransformIterator(BaseIteratorType base, std::function transform) + : m_base(base), m_transform(transform) + { + } + + TransformIterator& operator++() + { + ++m_base; + return *this; + } + + bool operator==(TransformIterator other) const { return m_base == other.m_base; } + bool operator!=(TransformIterator other) const { return !(m_base == other.m_base); } + TargetType operator*() { return m_transform(*m_base); } +};