-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Iwd project type #66
Iwd project type #66
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
#include "LinkerSearchPaths.h" | ||
#include "ObjContainer/IPak/IPakWriter.h" | ||
#include "ObjContainer/IWD/IWD.h" | ||
#include "ObjContainer/IWD/IWDWriter.h" | ||
#include "ObjLoading.h" | ||
#include "ObjWriting.h" | ||
#include "SearchPath/SearchPaths.h" | ||
|
@@ -45,15 +46,11 @@ enum class ProjectType | |
NONE, | ||
FASTFILE, | ||
IPAK, | ||
|
||
IWD, | ||
MAX | ||
}; | ||
|
||
constexpr const char* PROJECT_TYPE_NAMES[static_cast<unsigned>(ProjectType::MAX)]{ | ||
"none", | ||
"fastfile", | ||
"ipak", | ||
}; | ||
constexpr const char* PROJECT_TYPE_NAMES[static_cast<std::underlying_type_t<ProjectType>>(ProjectType::MAX)]{"none", "fastfile", "ipak", "iwd"}; | ||
|
||
class LinkerImpl final : public Linker | ||
{ | ||
|
@@ -462,6 +459,32 @@ class LinkerImpl final : public Linker | |
return true; | ||
} | ||
|
||
bool BuildIWD(const std::string& projectName, const ZoneDefinition& zoneDefinition, SearchPaths& assetSearchPaths) const | ||
{ | ||
const fs::path iwdFolderPath(m_args.GetOutputFolderPathForProject(projectName)); | ||
auto iwdFilePath(iwdFolderPath); | ||
const auto iwdFileName = zoneDefinition.m_name + ".iwd"; | ||
iwdFilePath.append(iwdFileName); | ||
|
||
fs::create_directories(iwdFolderPath); | ||
|
||
const auto iwdWriter = IWDWriter::Create(iwdFilePath.string(), &assetSearchPaths); | ||
for (const auto& assetEntry : zoneDefinition.m_assets) | ||
{ | ||
iwdWriter->AddFile(assetEntry.m_asset_name); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does that mean it does try to write every asset into the IWD with the asset name interpreted as the full path? That would mean that you cannot just simply use the same zone files as for the fastfile, right?
and it would refer to In the context of the IWD here it would probably throw a "File not found"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm that is true. Would that mean IWDs should have their own type of definition that is only paths to files? Because you can plausibly load any kind of asset from raw in games such as CoD4/T4. I suppose it could be handled by making it so each asset type that doesn't have the full path in its name has a folder: for example image asset to images folder this way you wouldn't need to specify the images folder in the file name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean while it's true that the older games support a lot of assets from IWDs, they should be consistent in what assets they put in which files, right? For example MW2 does only use IWDs for iwi images and streamed sounds. I think some older titles also have weapon files in IWDs. The way i'd do it is just have as much as possible in fastfiles and not in IWDs. At least for the first iteration. Or what do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I should add: The idea i had in mind on how to handle these other project types in the future was not to write different zone files necessarly. I added a thing called "assetlists" for IPaks previously for example. Like Linker would link a fastfile mod.ff and create an assetlists/mod.csv file which can then be refered to from the IWD zone file with That would require the way you define a zone and the way you define an IWD to match though 😅 |
||
} | ||
|
||
if (!iwdWriter->Write()) | ||
{ | ||
std::cerr << "Writing iwd failed." << std::endl; | ||
return false; | ||
} | ||
|
||
std::cout << "Created iwd \"" << iwdFilePath.string() << "\"\n"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason for mixing I know i did it but i try to stick to just |
||
|
||
return true; | ||
} | ||
|
||
bool BuildReferencedTargets(const std::string& projectName, const std::string& targetName, const ZoneDefinition& zoneDefinition) | ||
{ | ||
return std::all_of(zoneDefinition.m_targets_to_build.begin(), | ||
|
@@ -512,6 +535,10 @@ class LinkerImpl final : public Linker | |
result = BuildIPak(projectName, *zoneDefinition, assetSearchPaths); | ||
break; | ||
|
||
case ProjectType::IWD: | ||
result = BuildIWD(projectName, *zoneDefinition, assetSearchPaths); | ||
break; | ||
|
||
default: | ||
assert(false); | ||
result = false; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#include "IWDWriter.h" | ||
|
||
#include "../minizip/zip.h" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't use relative paths in includes 😅 If minizip is currently not in the include paths for "ObjWriting" you may need to add it in the premake script "ObjWriting.lua" |
||
|
||
#include <algorithm> | ||
#include <iostream> | ||
|
||
class IWDWriterImpl final : public IWDWriter | ||
{ | ||
public: | ||
explicit IWDWriterImpl(const std::string& iwdFileName, ISearchPath* assetSearchPath) | ||
: m_filename(iwdFileName), | ||
m_asset_search_path(assetSearchPath) | ||
{ | ||
m_files.clear(); | ||
m_added_files.clear(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clearing vectors in constructors is not necessary because the vector will be initialize by the automatically called default constructor of |
||
m_zipfile = zipOpen(m_filename.c_str(), APPEND_STATUS_CREATE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure you want to append? What if it already exists? Files duplicate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as i understood it, this does make exactly what you are expecting: It creates the file instead of appending. The Compared to the source: #define APPEND_STATUS_CREATE (0)
#define APPEND_STATUS_CREATEAFTER (1)
#define APPEND_STATUS_ADDINZIP (2) |
||
if (!m_zipfile) | ||
{ | ||
throw std::runtime_error("Error creating IWD file: \"" + m_filename + "\""); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to log this on std::cerr and have a graceful termination with non-zero exit code. And not being able to create a file may happen for example when having an invalid character in the path name. |
||
} | ||
} | ||
|
||
void AddFile(std::string fileName) override | ||
{ | ||
const auto existingEntry = std::find(m_added_files.cbegin(), m_added_files.cend(), fileName); | ||
if (existingEntry != m_added_files.cend()) | ||
{ | ||
std::cerr << "Duplicate entry in " << m_filename << "'s zone definition; ignoring" << "\n"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can just make it |
||
return; | ||
} | ||
m_files.emplace_back(fileName); | ||
} | ||
|
||
void AddFileToArchive(const std::string& fileName, const std::unique_ptr<char[]>& fileData, const size_t& fileSize) | ||
{ | ||
zip_fileinfo zi = {}; | ||
|
||
if (zipOpenNewFileInZip(m_zipfile, fileName.c_str(), &zi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION) != ZIP_OK) | ||
{ | ||
zipClose(m_zipfile, NULL); | ||
throw std::runtime_error("Could not open new file in IWD \"" + fileName + "\"\n"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't use |
||
} | ||
|
||
if (zipWriteInFileInZip(m_zipfile, fileData.get(), fileSize) != ZIP_OK) | ||
{ | ||
zipClose(m_zipfile, NULL); | ||
throw std::runtime_error("Could not write file data to IWD \"" + fileName + "\"\n"); | ||
} | ||
|
||
if (zipCloseFileInZip(m_zipfile) != ZIP_OK) | ||
{ | ||
zipClose(m_zipfile, NULL); | ||
throw std::runtime_error("Could not close file in IWD \"" + fileName + "\"\n"); | ||
} | ||
} | ||
|
||
std::unique_ptr<char[]> ReadFileDataFromSearchPath(const std::string& fileName, size_t& fileSize) const | ||
{ | ||
const auto openFile = m_asset_search_path->Open(fileName); | ||
if (!openFile.IsOpen()) | ||
{ | ||
std::cerr << "Could not open file for writing to IWD \"" << fileName << "\"\n"; | ||
return nullptr; | ||
} | ||
|
||
fileSize = static_cast<size_t>(openFile.m_length); | ||
auto fileData = std::make_unique<char[]>(fileSize); | ||
openFile.m_stream->read(fileData.get(), fileSize); | ||
|
||
return fileData; | ||
} | ||
|
||
bool WriteFileData(const std::string& fileName) | ||
{ | ||
size_t fileSize; | ||
const auto fileData = ReadFileDataFromSearchPath(fileName, fileSize); | ||
if (!fileData) | ||
return false; | ||
|
||
AddFileToArchive(fileName, fileData, fileSize); | ||
|
||
return true; | ||
} | ||
|
||
bool Write() override | ||
{ | ||
auto result = false; | ||
try | ||
{ | ||
result = std::all_of(m_files.cbegin(), | ||
m_files.cend(), | ||
[this](const std::string& fileName) | ||
{ | ||
return WriteFileData(fileName); | ||
}); | ||
} | ||
catch (const std::exception& e) | ||
{ | ||
std::cerr << "Error while creating IWD " << m_filename << " : " << e.what(); | ||
result = false; | ||
} | ||
|
||
zipClose(m_zipfile, NULL); | ||
return result; | ||
} | ||
|
||
private: | ||
const std::string m_filename; | ||
|
||
ISearchPath* m_asset_search_path; | ||
std::vector<std::string> m_files; | ||
std::vector<std::string> m_added_files; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use an Sets make use of hashing and are therefore magnitudes faster for this. |
||
|
||
zipFile m_zipfile; | ||
}; | ||
|
||
std::unique_ptr<IWDWriter> IWDWriter::Create(const std::string& iwdFileName, ISearchPath* assetSearchPath) | ||
{ | ||
return std::make_unique<IWDWriterImpl>(iwdFileName, assetSearchPath); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#pragma once | ||
#include "SearchPath/ISearchPath.h" | ||
|
||
#include <memory> | ||
#include <ostream> | ||
|
||
class IWDWriter | ||
{ | ||
public: | ||
static constexpr auto USE_COMPRESSION = true; | ||
|
||
IWDWriter() = default; | ||
virtual ~IWDWriter() = default; | ||
|
||
IWDWriter(const IWDWriter& other) = default; | ||
IWDWriter(IWDWriter&& other) noexcept = default; | ||
IWDWriter& operator=(const IWDWriter& other) = default; | ||
IWDWriter& operator=(IWDWriter&& other) noexcept = default; | ||
|
||
virtual void AddFile(std::string fileName) = 0; | ||
virtual bool Write() = 0; | ||
|
||
static std::unique_ptr<IWDWriter> Create(const std::string& iwdFileName, ISearchPath* assetSearchPath); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately clang-format does not behave particularly cool when it comes to array initializer lists.
You can force it to make way less awkward formatting by adding a trailing comma
,
after the last entry which is what i do everywhere where i catch it until they introduce a better way to handle this 😅