mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
ZoneFilePack reads and writes the editor's packed files: named entries whose data is sd0, one entry for a single file or the zone, terrain, scenes and triggers of a whole map. Every length is checked, data that is not sd0 or has a chunk over 256 KiB is refused, and only a name's base name is used. ZoneFilePush makes a set of what was sent (one file replaces that file in the zone's set), checks it and applies it like any other set. The override store links a set's files instead of moving them, so a file the zone already has can be carried into its next set, and one change at a time is made in an override folder whichever server makes it. The asset manager can read the client's own file past an override. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
152 lines
4.2 KiB
C++
152 lines
4.2 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <filesystem>
|
|
#include <optional>
|
|
#include <string_view>
|
|
|
|
#include "Pack.h"
|
|
#include "PackIndex.h"
|
|
|
|
enum class eAssetBundleType {
|
|
None,
|
|
Unpacked,
|
|
Packed
|
|
};
|
|
|
|
struct AssetMemoryBuffer : std::streambuf {
|
|
char* m_Base;
|
|
bool m_Success;
|
|
|
|
AssetMemoryBuffer(char* base, std::ptrdiff_t n, bool success) {
|
|
m_Base = base;
|
|
m_Success = success;
|
|
if (!m_Success) return;
|
|
this->setg(base, base, base + n);
|
|
}
|
|
|
|
~AssetMemoryBuffer() {
|
|
if (m_Success) free(m_Base);
|
|
}
|
|
|
|
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override {
|
|
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
|
|
}
|
|
|
|
pos_type seekoff(off_type off,
|
|
std::ios_base::seekdir dir,
|
|
std::ios_base::openmode which = std::ios_base::in) override {
|
|
if (dir == std::ios_base::cur)
|
|
gbump(off);
|
|
else if (dir == std::ios_base::end)
|
|
setg(eback(), egptr() + off, egptr());
|
|
else if (dir == std::ios_base::beg)
|
|
setg(eback(), eback() + off, egptr());
|
|
return gptr() - eback();
|
|
}
|
|
|
|
// Every byte of the file (empty when it wasn't found)
|
|
std::string_view Bytes() const {
|
|
if (!m_Success) return {};
|
|
return std::string_view(eback(), static_cast<size_t>(egptr() - eback()));
|
|
}
|
|
};
|
|
|
|
struct AssetStream : std::istream {
|
|
AssetStream(char* base, std::ptrdiff_t n, bool success) : std::istream(new AssetMemoryBuffer(base, n, success)) {}
|
|
|
|
~AssetStream() {
|
|
delete rdbuf();
|
|
}
|
|
|
|
operator bool() {
|
|
return reinterpret_cast<AssetMemoryBuffer*>(rdbuf())->m_Success;
|
|
}
|
|
|
|
// Every byte of the file, whatever has been read of it so far
|
|
std::string_view Bytes() const {
|
|
return reinterpret_cast<const AssetMemoryBuffer*>(rdbuf())->Bytes();
|
|
}
|
|
};
|
|
|
|
class AssetManager {
|
|
public:
|
|
AssetManager(const std::filesystem::path& path);
|
|
|
|
[[nodiscard]]
|
|
const std::filesystem::path& GetResPath() const {
|
|
return m_ResPath;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
eAssetBundleType GetAssetBundleType() const {
|
|
return m_AssetBundleType;
|
|
}
|
|
|
|
/**
|
|
* A folder of edited zone files (zone_override_dir): a file under maps/ is read from <dir>/maps/... when it is
|
|
* there, and from the client otherwise. Nothing else is looked for in it, and the client's files are never
|
|
* written. Empty turns it off.
|
|
*/
|
|
void SetOverrideDir(const std::filesystem::path& dir);
|
|
|
|
[[nodiscard]]
|
|
const std::filesystem::path& GetOverrideDir() const {
|
|
return m_OverrideDir;
|
|
}
|
|
|
|
// Where name is read from in the override folder: only for a name under maps/ whose file is there
|
|
[[nodiscard]]
|
|
std::optional<std::filesystem::path> GetOverridePath(std::string name) const;
|
|
|
|
/**
|
|
* The path inside an override folder a name under maps/ is kept at (lowercase, forward slashes), whether or not
|
|
* a file is there. nullopt for any other name and for one with an empty, "." or ".." part.
|
|
*/
|
|
[[nodiscard]]
|
|
static std::optional<std::string> OverrideName(std::string name);
|
|
|
|
[[nodiscard]]
|
|
bool HasFile(std::string name) const;
|
|
|
|
[[nodiscard]]
|
|
bool GetFile(std::string name, char** data, uint32_t* len) const;
|
|
|
|
// Where GetFile reads name from when it is a loose file in res; nullopt when it comes from a pack or isn't there
|
|
[[nodiscard]]
|
|
std::optional<std::filesystem::path> GetLoosePath(std::string name) const;
|
|
|
|
[[nodiscard]]
|
|
AssetStream GetFile(const char* name) const;
|
|
|
|
// The client's own file, whatever the override folder holds for the name
|
|
[[nodiscard]]
|
|
AssetStream GetClientFile(const std::string& name) const;
|
|
|
|
[[nodiscard]]
|
|
bool HasClientFile(const std::string& name) const;
|
|
|
|
[[nodiscard]]
|
|
AssetStream GetFile(const std::string& name) const { return GetFile(name.c_str()); };
|
|
|
|
private:
|
|
void LoadPackIndex();
|
|
bool HasFile(std::string name, bool useOverride) const;
|
|
bool GetFile(std::string name, char** data, uint32_t* len, bool useOverride) const;
|
|
|
|
// Modified crc algorithm (mpeg2)
|
|
// Reference: https://stackoverflow.com/questions/54339800/how-to-modify-crc-32-to-crc-32-mpeg-2
|
|
static inline uint32_t crc32b(uint32_t crc, std::string_view message);
|
|
|
|
std::filesystem::path m_Path;
|
|
std::filesystem::path m_RootPath;
|
|
std::filesystem::path m_ResPath;
|
|
std::filesystem::path m_OverrideDir;
|
|
|
|
eAssetBundleType m_AssetBundleType = eAssetBundleType::None;
|
|
|
|
std::optional<PackIndex> m_PackIndex;
|
|
};
|