Skip to content

Commit

Permalink
refactor(Archive): initial implementation of spec-compliance
Browse files Browse the repository at this point in the history
This patch adapts the implementation of `ReadArchive` to properly support object references in archives.

To do this, I added a new `Object` class which represents any ZenGin object and a new `read_object` API to `ReadArchive` which facilitates loading of a set of pre-defined ZenGin object types. This new API takes care of creating new object instances and resolving archive-internal references.

This required changing the `VirtualObject` hierarchy to use `shared_ptr` instead of `unique_ptr` so the system is able to share references to archive objects without sacrificing memory safety.
  • Loading branch information
lmichaelis committed Nov 18, 2023
1 parent ad83918 commit 5a15d3d
Show file tree
Hide file tree
Showing 24 changed files with 808 additions and 589 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ list(APPEND _ZK_SOURCES
src/ModelScriptDsl.cc
src/MorphMesh.cc
src/MultiResolutionMesh.cc
src/Object.cc
src/SaveGame.cc
src/SoftSkinMesh.cc
src/Stream.cc
Expand Down
20 changes: 17 additions & 3 deletions include/zenkit/Archive.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#pragma once
#include "zenkit/Boxes.hh"
#include "zenkit/Library.hh"
#include "zenkit/Object.hh"
#include "zenkit/Stream.hh"

#include "phoenix/buffer.hh"
Expand All @@ -11,9 +12,7 @@
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>

#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <variant>

Expand All @@ -23,6 +22,7 @@ namespace phoenix {

namespace zenkit {
class Read;
class ReadArchive;

enum class ArchiveFormat {
BINARY = 0,
Expand Down Expand Up @@ -145,6 +145,19 @@ namespace zenkit {
/// \throws zenkit::ParserError
static std::unique_ptr<ReadArchive> from(Read* r);

template <typename T>
std::enable_if_t<std::is_base_of_v<Object, T>, std::shared_ptr<T>> //
read_object(GameVersion version) {
auto obj = this->read_object(version);
if (obj != nullptr && obj->get_type() != T::TYPE) {
throw ParserError {"ReadArchive", "Read unexcected object!"};
}

return std::reinterpret_pointer_cast<T>(std::move(obj));
}

std::shared_ptr<Object> read_object(GameVersion version);

/// \brief Tries to read the begin of a new object from the archive.
///
/// If a beginning of an object could not be read, the internal buffer is reverted to the state
Expand Down Expand Up @@ -240,7 +253,7 @@ namespace zenkit {
}

/// \return Whether or not this archive represents a save-game.
[[nodiscard]] inline bool is_save_game() const noexcept {
[[nodiscard]] bool is_save_game() const noexcept {
return header.save;
}

Expand All @@ -259,6 +272,7 @@ namespace zenkit {
Read* read;

private:
std::unordered_map<uint32_t, std::shared_ptr<Object>> _m_cache {};
std::unique_ptr<Read> _m_owned;
};
} // namespace zenkit
80 changes: 80 additions & 0 deletions include/zenkit/Object.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright © 2023 GothicKit Contributors.
// SPDX-License-Identifier: MIT
#pragma once
#include "Misc.hh"

namespace zenkit {
class ReadArchive;

enum class ObjectType {
zCVob = 0, ///< The base type for all VObs.
zCVobLevelCompo = 1, ///< A basic VOb used for grouping other VObs.
oCItem = 2, ///< A VOb representing an item
oCNpc = 3, ///< A VOb representing an NPC
zCMoverController = 4,
zCVobScreenFX = 5,
zCVobStair = 6,
zCPFXController = 7,
zCVobAnimate = 8,
zCVobLensFlare = 9,
zCVobLight = 10,
zCVobSpot = 11,
zCVobStartpoint = 12,
zCMessageFilter = 13,
zCCodeMaster = 14,
zCTriggerWorldStart = 15,
zCCSCamera = 16,
zCCamTrj_KeyFrame = 17,
oCTouchDamage = 18,
zCTriggerUntouch = 19,
zCEarthquake = 20,
oCMOB = 21, ///< The base VOb type used for dynamic world objects.
oCMobInter = 22, ///< The base VOb type used for interactive world objects.
oCMobBed = 23, ///< A bed the player can sleep in.
oCMobFire = 24, ///< A campfire the player can cook things on.
oCMobLadder = 25, ///< A ladder the player can climb.
oCMobSwitch = 26, ///< A switch or button the player can use.
oCMobWheel = 27, ///< A grindstone the player can sharpen their weapon with.
oCMobContainer = 28, ///< A container the player can open.
oCMobDoor = 29, ///< A door the player can open.
zCTrigger = 30, ///< The base VOb type used for all kinds of triggers.
zCTriggerList = 31, ///< A collection of multiple triggers.
oCTriggerScript = 32, ///< A trigger for calling a script function.
oCTriggerChangeLevel = 33, ///< A trigger for changing the game world.
oCCSTrigger = 34, ///< A cutscene trigger.
zCMover = 35,
zCVobSound = 36, ///< A VOb which emits a certain sound.
zCVobSoundDaytime = 37, ///< A VOb which emits a sound only during a specified time.
oCZoneMusic = 38, ///< A VOb which plays music from the soundtrack.
oCZoneMusicDefault = 39,
zCZoneZFog = 40, ///< A VOb which indicates a foggy area.
zCZoneZFogDefault = 41,
zCZoneVobFarPlane = 42,
zCZoneVobFarPlaneDefault = 43,
ignored = 44,
unknown = 45,

oCNpcTalent,
zCEventManager,
zCDecal,
zCMesh,
zCProgMeshProto,
zCParticleFX,
zCAICamera,
zCModel,
zCMorphMesh,
oCAIHuman,
oCAIVobMove,
oCCSPlayer,
zCSkyControler_Outdoor
};

class Object ZKAPI {
public:
Object() = default;
virtual ~Object() noexcept = default;
[[nodiscard]] virtual ObjectType get_type() const = 0;

virtual void load(ReadArchive& r, GameVersion version);
};
} // namespace zenkit
62 changes: 61 additions & 1 deletion include/zenkit/World.hh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,54 @@ namespace phoenix {
}

namespace zenkit {
namespace vobs {
struct Npc;
}

struct CutscenePlayer : Object {
static constexpr ObjectType TYPE = ObjectType::oCCSPlayer;

int32_t last_process_day;
int32_t last_process_hour;
int32_t play_list_count;

[[nodiscard]] ObjectType get_type() const override {
return TYPE;
}

void load(ReadArchive& r, GameVersion version) override;
};

struct SkyController : Object {
static constexpr ObjectType TYPE = ObjectType::zCSkyControler_Outdoor;

float master_time;
float rain_weight;
float rain_start;
float rain_stop;
float rain_sct_timer;
float rain_snd_vol;
float day_ctr;

// Only relevant for G1:
float fade_scale;
bool render_lightning;
bool is_raining;
int rain_ctr;

[[nodiscard]] ObjectType get_type() const override {
return TYPE;
}

void load(ReadArchive& r, GameVersion version) override;
};

struct SpawnLocation {
std::shared_ptr<vobs::Npc> npc;
glm::vec3 position;
float timer;
};

/// \brief Represents a ZenGin world.
class World {
public:
Expand Down Expand Up @@ -76,7 +124,7 @@ namespace zenkit {

public:
/// \brief The list of VObs defined in this world.
std::vector<std::unique_ptr<VirtualObject>> world_vobs;
std::vector<std::shared_ptr<VirtualObject>> world_vobs;

/// \brief The mesh of the world.
Mesh world_mesh;
Expand All @@ -86,5 +134,17 @@ namespace zenkit {

/// \brief The way-net of this world.
WayNet world_way_net;

// \note Only available in save-games, otherwise empty.
std::vector<std::shared_ptr<vobs::Npc>> npcs {};
std::vector<SpawnLocation> npc_spawns {};
bool npc_spawn_enabled = false;
int npc_spawn_flags = 0;

// \note Only available in save-games, otherwise null.
std::shared_ptr<CutscenePlayer> player;

// \note Only available in save-games, otherwise null.
std::shared_ptr<SkyController> sky_controller;
};
} // namespace zenkit
12 changes: 9 additions & 3 deletions include/zenkit/vobs/Camera.hh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ namespace zenkit {

namespace vobs {
/// \brief A VOb which describes the trajectory of a camera during a cutscene.
struct CameraTrajectoryFrame : public VirtualObject {
struct CameraTrajectoryFrame : VirtualObject {
static constexpr ObjectType TYPE = ObjectType::zCCamTrj_KeyFrame;

float time;
float roll_angle;
float fov_scale;
Expand All @@ -93,11 +95,15 @@ namespace zenkit {
ZKREM("use ::load()")
ZKAPI static std::unique_ptr<CameraTrajectoryFrame> parse(ReadArchive& ctx, GameVersion version);

[[nodiscard]] ObjectType get_type() const override {
return TYPE;
}

ZKAPI void load(ReadArchive& r, GameVersion version) override;
};

/// \brief A VOb which defined the movement of the camera during a cutscene.
struct CutsceneCamera : public VirtualObject {
struct CutsceneCamera : VirtualObject {
CameraTrajectory trajectory_for;
CameraTrajectory target_trajectory_for;
CameraLoop loop_mode;
Expand All @@ -115,7 +121,7 @@ namespace zenkit {
std::int32_t position_count;
std::int32_t target_count;

std::vector<std::unique_ptr<CameraTrajectoryFrame>> frames;
std::vector<std::shared_ptr<CameraTrajectoryFrame>> frames;

// Save-game only variables
bool s_paused {false};
Expand Down
2 changes: 1 addition & 1 deletion include/zenkit/vobs/Light.hh
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace zenkit {
};

/// \brief A VOb which acts as a light source.
struct Light : public VirtualObject, public LightPreset {
struct Light : VirtualObject, LightPreset {
/// \brief Parses a light VOb the given *ZenGin* archive.
/// \param[out] obj The object to read.
/// \param[in,out] ctx The archive reader to read from.
Expand Down
Loading

0 comments on commit 5a15d3d

Please sign in to comment.