415 lines
14 KiB
C++
415 lines
14 KiB
C++
#include <std_include.hpp>
|
|
|
|
#include "assets/assets.hpp"
|
|
#include "irawfile.hpp"
|
|
|
|
#include "utils/compression.hpp"
|
|
#include <utils/io.hpp>
|
|
#include <utils/stream.hpp>
|
|
#include <utils/string.hpp>
|
|
|
|
namespace iw4of::interfaces
|
|
{
|
|
bool irawfile::write_internal(const native::XAssetHeader& header) const
|
|
{
|
|
RETURN_IF_NULL(header.rawfile);
|
|
RETURN_IF_NULL(header.rawfile->buffer);
|
|
|
|
if (header.rawfile->len > 0)
|
|
{
|
|
std::string uncompressed_buffer;
|
|
if (header.rawfile->compressedLen > 0)
|
|
{
|
|
const auto& str = utils::compression::zlib::decompress(std::string(header.rawfile->buffer, header.rawfile->compressedLen));
|
|
uncompressed_buffer = std::string(local_allocator.duplicate_string(str), header.rawfile->len);
|
|
}
|
|
else
|
|
{
|
|
uncompressed_buffer = std::string(header.rawfile->buffer, header.rawfile->len);
|
|
}
|
|
|
|
write_named_assets(header.rawfile->name, uncompressed_buffer);
|
|
|
|
const auto work_path = get_work_path(header).string();
|
|
const auto data = std::string(uncompressed_buffer.data(), header.rawfile->len);
|
|
return utils::io::write_file(work_path, data);
|
|
}
|
|
|
|
// Happens
|
|
return true;
|
|
}
|
|
|
|
void* irawfile::read_internal(const std::string& name) const
|
|
{
|
|
const auto& path = get_work_path(name).string();
|
|
|
|
if (!utils::io::file_exists(path))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
const auto& contents = utils::io::read_file(path);
|
|
|
|
auto* asset = local_allocator.allocate<native::RawFile>();
|
|
|
|
RETURN_IF_NULL(asset);
|
|
|
|
asset->name = local_allocator.duplicate_string(name);
|
|
asset->len = static_cast<int32_t>(contents.size());
|
|
|
|
read_named_assets(asset->name, contents);
|
|
|
|
const auto& compressedData = utils::compression::zlib::compress(contents);
|
|
|
|
// Only save the compressed buffer if we gained space
|
|
if (compressedData.size() < asset->len)
|
|
{
|
|
asset->buffer = local_allocator.allocate_array<char>(compressedData.size());
|
|
std::memcpy(const_cast<char*>(asset->buffer), compressedData.data(), compressedData.size());
|
|
asset->compressedLen = static_cast<int32_t>(compressedData.size());
|
|
}
|
|
else
|
|
{
|
|
asset->buffer = local_allocator.allocate_array<char>(contents.size() + 1);
|
|
std::memcpy(const_cast<char*>(asset->buffer), contents.data(), contents.size());
|
|
asset->compressedLen = 0;
|
|
}
|
|
|
|
return asset;
|
|
}
|
|
|
|
void irawfile::write_named_assets(const std::string& script_name, const std::string& script_data) const
|
|
{
|
|
const auto children = get_child_assets(script_name, script_data);
|
|
for(const auto& child : children)
|
|
{
|
|
assets->write(child.type, child.header.data);
|
|
}
|
|
}
|
|
|
|
void irawfile::read_named_assets(const std::string& script_name, const std::string& script_data) const {
|
|
|
|
// And this should be enough
|
|
get_child_assets(script_name, script_data);
|
|
}
|
|
|
|
std::vector<native::XAsset> irawfile::get_child_assets(const native::XAssetHeader& header) const
|
|
{
|
|
const std::string script_name = header.rawfile->name;
|
|
std::string script_data;
|
|
|
|
// Decompress
|
|
if (header.rawfile->compressedLen > 0)
|
|
{
|
|
const auto& str = utils::compression::zlib::decompress(std::string(header.rawfile->buffer, header.rawfile->compressedLen));
|
|
script_data = std::string(local_allocator.duplicate_string(str), header.rawfile->len);
|
|
}
|
|
else
|
|
{
|
|
script_data = std::string(header.rawfile->buffer, header.rawfile->len);
|
|
}
|
|
|
|
return get_child_assets(script_name, script_data);
|
|
}
|
|
|
|
std::vector<native::XAsset> irawfile::get_child_assets(const std::string& script_name, const std::string& script_data) const
|
|
{
|
|
auto result = std::vector<native::XAsset>();
|
|
|
|
if (script_name.ends_with(".gsc"))
|
|
{
|
|
if (script_name.ends_with("_fx.gsc"))
|
|
{
|
|
if (script_name.starts_with("maps/createfx/"))
|
|
{
|
|
// Createfx
|
|
const auto sounds = get_create_fx_sounds(script_data);
|
|
for (const auto& sound : sounds)
|
|
{
|
|
result.push_back({native::ASSET_TYPE_SOUND, sound});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// General FX
|
|
const auto fxs = get_map_fx(script_data);
|
|
for (const auto& fx : fxs)
|
|
{
|
|
result.push_back({native::ASSET_TYPE_FX, fx});
|
|
}
|
|
}
|
|
}
|
|
else if (script_name.ends_with("_precache.gsc"))
|
|
{
|
|
const auto precache = get_assets_in_precache(script_data);
|
|
for (const auto& asset : precache)
|
|
{
|
|
result.push_back(asset);
|
|
}
|
|
}
|
|
|
|
const auto anim_trees = get_map_animtrees(script_data);
|
|
for (const auto& anim_tree : anim_trees)
|
|
{
|
|
result.push_back({native::ASSET_TYPE_RAWFILE, anim_tree});
|
|
}
|
|
|
|
const auto ambient = get_ambient_play(script_data);
|
|
if (ambient)
|
|
{
|
|
result.push_back({native::ASSET_TYPE_SOUND, ambient});
|
|
}
|
|
}
|
|
else if (script_name.ends_with(".atr"))
|
|
{
|
|
const auto anims = get_animtree_anims(script_data);
|
|
for (const auto& anim : anims)
|
|
{
|
|
result.push_back({native::ASSET_TYPE_XANIMPARTS, anim});
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<native::XAnimParts*> irawfile::get_animtree_anims(const std::string& script) const
|
|
{
|
|
auto result = std::vector<native::XAnimParts*>();
|
|
std::istringstream iss(script);
|
|
|
|
for (std::string line; std::getline(iss, line);)
|
|
{
|
|
line = line.erase(0, line.find_first_not_of(" \n\r\t"));
|
|
while (line.length() > 0 && (line.ends_with("\n") || line.ends_with("\r") || line.ends_with(" ")))
|
|
{
|
|
line.pop_back();
|
|
}
|
|
|
|
if (line.length() > 0 && line[0] != '/' && line[1] != '/')
|
|
{
|
|
const auto parts = find<native::XAnimParts>(iw4of::native::XAssetType::ASSET_TYPE_XANIMPARTS, line);
|
|
if (parts)
|
|
{
|
|
result.push_back(parts);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<native::RawFile*> irawfile::get_map_animtrees(const std::string& script) const
|
|
{
|
|
static std::regex animtree_catcher("#using_animtree\\(\"(.*)\"\\);");
|
|
std::smatch m;
|
|
|
|
std::string::const_iterator search_start(script.cbegin());
|
|
std::vector<native::RawFile*> trees{};
|
|
|
|
while (std::regex_search(search_start, script.cend(), m, animtree_catcher))
|
|
{
|
|
if (m.size() > 1)
|
|
{
|
|
const auto& match = m[1];
|
|
auto animtree_name = std::format("animtrees/{}.atr", match.str());
|
|
auto animtree = find<native::RawFile>(native::ASSET_TYPE_RAWFILE, animtree_name);
|
|
search_start = m.suffix().first;
|
|
|
|
if (animtree)
|
|
{
|
|
auto new_name = std::string(animtree->name);
|
|
trees.push_back(animtree);
|
|
}
|
|
else
|
|
{
|
|
print_error("Could not find animtree {}\n", animtree_name);
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
return trees;
|
|
}
|
|
|
|
std::vector<native::XAnimParts*> irawfile::get_map_animated_model_anims(const std::string& script) const
|
|
{
|
|
// We use % because it's used for MP and SP (on destructibles for instance it's for both)
|
|
// but we also have to use "" for mp_specific anims
|
|
static std::regex anim_catchers[] = {
|
|
std::regex("%(.*);"), // SP & MP sometimes
|
|
std::regex("\"\\] *= \"*(.*)\";") // MP only
|
|
};
|
|
|
|
std::vector<native::XAnimParts*> anim_parts{};
|
|
|
|
for (size_t i = 0; i < ARRAYSIZE(anim_catchers); i++)
|
|
{
|
|
const auto& anim_catcher = anim_catchers[i];
|
|
std::smatch m;
|
|
std::string::const_iterator search_start(script.cbegin());
|
|
while (std::regex_search(search_start, script.cend(), m, anim_catcher))
|
|
{
|
|
if (m.size() > 1)
|
|
{
|
|
const auto& match = m[1];
|
|
auto anim_name = match.str();
|
|
auto anim = find<native::XAnimParts>(native::ASSET_TYPE_XANIMPARTS, anim_name);
|
|
search_start = m.suffix().first;
|
|
|
|
if (anim)
|
|
{
|
|
anim_parts.push_back(anim);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return anim_parts;
|
|
}
|
|
|
|
native::snd_alias_list_t* irawfile::get_ambient_play(const std::string& script) const
|
|
{
|
|
static std::regex ambientplay("ambientplay\\( \"((.*))\" \\);");
|
|
std::smatch matches;
|
|
|
|
if (std::regex_search(script.cbegin(), script.cend(), matches, ambientplay))
|
|
{
|
|
if (matches.size() > 1)
|
|
{
|
|
const auto& match = matches[1];
|
|
auto sound_name = match.str();
|
|
|
|
auto sound = find<native::snd_alias_list_t>(native::ASSET_TYPE_SOUND, sound_name);
|
|
|
|
if (sound)
|
|
{
|
|
return sound;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<native::FxEffectDef*> irawfile::get_map_fx(const std::string& script) const
|
|
{
|
|
static std::regex fx_catcher("\\] = loadfx\\( *\"(.+)\" *\\);");
|
|
std::smatch m;
|
|
std::string::const_iterator search_start(script.cbegin());
|
|
|
|
std::vector<native::FxEffectDef*> fxs{};
|
|
|
|
while (std::regex_search(search_start, script.cend(), m, fx_catcher))
|
|
{
|
|
bool skip = true;
|
|
for (auto match : m)
|
|
{
|
|
if (skip)
|
|
{
|
|
skip = false;
|
|
continue;
|
|
}
|
|
|
|
auto fx_name = match.str();
|
|
auto fx = find<native::FxEffectDef>(native::ASSET_TYPE_FX, fx_name);
|
|
search_start = m.suffix().first;
|
|
|
|
if (fx)
|
|
{
|
|
fxs.push_back(fx);
|
|
}
|
|
}
|
|
}
|
|
|
|
return fxs;
|
|
}
|
|
|
|
std::vector<native::XAsset> irawfile::get_assets_in_precache(const std::string& script_contents) const
|
|
{
|
|
static std::regex anim_script_catcher(" *(.*)::main\\(\\);");
|
|
std::smatch anim_script_matches;
|
|
|
|
std::vector<native::XAsset> asset_list{};
|
|
|
|
std::string::const_iterator search_start(script_contents.cbegin());
|
|
while (std::regex_search(search_start, script_contents.cend(), anim_script_matches, anim_script_catcher))
|
|
{
|
|
if (anim_script_matches.size() > 1)
|
|
{
|
|
const auto& match = anim_script_matches[1];
|
|
auto script_name = std::format("{}.gsc", match.str());
|
|
std::replace(script_name.begin(), script_name.end(), '\\', '/');
|
|
|
|
search_start = anim_script_matches.suffix().first;
|
|
|
|
auto script = find<native::RawFile>(native::ASSET_TYPE_RAWFILE, script_name);
|
|
|
|
if (script)
|
|
{
|
|
asset_list.push_back({native::ASSET_TYPE_RAWFILE, script});
|
|
|
|
std::string contents = script->buffer;
|
|
if (script->compressedLen)
|
|
{
|
|
contents = utils::compression::zlib::decompress(std::string(script->buffer, script->compressedLen));
|
|
}
|
|
|
|
const auto& anim_trees = get_map_animtrees(contents);
|
|
const auto& model_anims = get_map_animated_model_anims(contents);
|
|
|
|
for (const auto& tree : anim_trees)
|
|
{
|
|
asset_list.push_back({native::ASSET_TYPE_RAWFILE, tree});
|
|
}
|
|
|
|
for (const auto& anims : model_anims)
|
|
{
|
|
asset_list.push_back({native::ASSET_TYPE_XANIMPARTS, anims});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return asset_list;
|
|
}
|
|
|
|
std::vector<native::snd_alias_list_t*> irawfile::get_create_fx_sounds(const std::string& script) const
|
|
{
|
|
static std::regex sound_catcher("(?:\\.v\\[\"soundalias\"\\] *= *\"(.+)\")");
|
|
std::smatch m;
|
|
|
|
std::vector<native::snd_alias_list_t*> sounds{};
|
|
|
|
std::string::const_iterator search_start(script.cbegin());
|
|
|
|
while (std::regex_search(search_start, script.cend(), m, sound_catcher))
|
|
{
|
|
bool skip = true;
|
|
for (auto match : m)
|
|
{
|
|
if (skip)
|
|
{
|
|
skip = false;
|
|
continue;
|
|
}
|
|
|
|
auto sound_name = match.str();
|
|
auto sound = find<native::snd_alias_list_t>(native::ASSET_TYPE_SOUND, sound_name);
|
|
search_start = m.suffix().first;
|
|
|
|
if (sound)
|
|
{
|
|
sounds.push_back(sound);
|
|
}
|
|
}
|
|
}
|
|
|
|
return sounds;
|
|
}
|
|
|
|
std::filesystem::path irawfile::get_file_name(const std::string& basename) const
|
|
{
|
|
return basename;
|
|
}
|
|
} // namespace iw4of::interfaces
|