Skip to content

Commit

Permalink
feat(libespm): Added data types def, Added more WRLD fields (skyrim-m…
Browse files Browse the repository at this point in the history
  • Loading branch information
kkEngine authored Apr 8, 2024
1 parent f1d9f28 commit 2471214
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 5 deletions.
26 changes: 26 additions & 0 deletions libespm/include/libespm/DataTypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

namespace espm {

using RecordFlagsType = std::uint32_t;

/// <summary>
/// A ulong used to identify a data object. May refer to a data object from a
/// mod or new object created in-game.
/// </summary>
using formId = std::uint32_t;

/// <summary>
/// 'Localized string', a ulong that is used as an index to look up string data
/// information in one of the string tables. Note that the record header for
/// the TES4 record indicates if the file is localized or not. If not, all
/// lstrings are zstrings.
/// </summary>
using lstring = std::uint32_t;

/// <summary>
/// Zero terminated string
/// </summary>
using zstring = std::string;

}
3 changes: 2 additions & 1 deletion libespm/include/libespm/RecordFlags.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#pragma once
#include "DataTypes.h"
#include <cstdint>

namespace espm {

enum RecordFlags : uint32_t
enum RecordFlags : RecordFlagsType
{
Deleted = 0x00000020,
Constant = 0x00000040,
Expand Down
1 change: 1 addition & 0 deletions libespm/include/libespm/RecordHeader.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include "CompressedFieldsCache.h"
#include "DataTypes.h"
#include "Type.h"

#pragma pack(push, 1)
Expand Down
126 changes: 124 additions & 2 deletions libespm/include/libespm/WRLD.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ class WRLD final : public RecordHeader
public:
static constexpr auto kType = "WRLD";

enum Flags
enum class RecordFlags : RecordFlagsType
{
CantWait = 0x80000
};

enum class Flags : uint8_t
{
None = 0,
SmallWorld = 0x01,
CantFastTravelFromHere = 0x02,
unknown = 0x04,
Expand All @@ -22,9 +28,125 @@ class WRLD final : public RecordHeader
NoGrass = 0x80
};

enum class UseParentFlags : std::uint16_t
{
None = 0,
UseLandData = 0x01, // Use Land Data (DNAM)
UseLODData = 0x02, // Use LOD Data (NAM3, NAM4)
UseMapData = 0x04, // Use Map Data (MNAM, MODL)
UseWaterData = 0x08, // Use Water Data (NAM2)
unknown = 0x10,
UseClimateData = 0x20, // Use Climate Data (CNAM)
UseSkyCell = 0x40, // Use Sky Cell
};

struct MapData
{
struct MapSize
{
// The usable width of the map
uint32_t width = 0;

// The usable height of the map
uint32_t height = 0;

// (my guess is) max North-West x coord
int16_t nwCellX = 0;

// (my guess is) max North-West y coord
int16_t nwCellY = 0;

// (my guess is) max South-EAST x coord
int16_t seCellX = 0;

// (my guess is) max South-EAST y coord
int16_t seCellY = 0;
};

MapSize mapSize = {};

// Camera Data (default 50000), new as of Skyrim 1.8, purpose is not yet
// known. (in my tests, this param controls min and max camera zoom)
float minCameraHeight = 50'000;
float maxCameraHeight = 80'000;

// Camera Data (default 50) (camera zoom pitch?)
float initialCameraPitch = 50;
};

struct Data
{
uint8_t flags = 0;
// The name of this worldspace used in the game
lstring localNameIndex = 0;

// X, Y
int16_t centerCellXY[2] = {};

// Interior Lighting reference (LGTM)
formId interiorLightingId = 0;

// Encounter Zone reference (ECZN)
formId encounterZoneId = 0;

// Location reference (LCTN)
formId exitLocationId = 0;

// Climate reference (CLMT)
formId climateId = 0;

// Water reference (WATR)
formId waterId = 0;

// LOD water-type, always a WATR form ID
formId waterLODTypeId = 0;

// LOD oceanwater-level (-14000.0 for Tamriel)
float waterLODHeight = 0;

// Default land- and oceanwater-levels (-27000 & -14000.0 for Tamriel)
float LandData[2] = {};

// Cloud Model - World model filename (part of MODL struct)
zstring cloudModelFileName;

// 16 or 28 byte structure
MapData mapData = {};

// Distant LOD Multiplier
float distantLODMult = 0;

Flags flags = Flags::None;

// Coordinates for the bottom left (South-West) corner of the worldspace
// (assumed from Oblivion)
int32_t bottomLeftCoord[2] = {};

// Coordinates for the top right (North-East) corner of the worldspace
// (assumed from Oblivion)
int32_t topRightCoord[2] = {};

// Form ID of the parent worldspace
formId parentWorldspaceId = 0;

// Use flags - Set if parts are inherited from parent worldspace WNAM
UseParentFlags useFlags = UseParentFlags::None;

// This field specifies where map markers will appear in relation to the
// parent (World Map Scale - -1=Hide Map Markers) (Cell X Offset * 4096)
// (Cell Y Offset * 4096) (Cell Z Offset * 4096)
float mapMarkerData[4] = {};

// The name of a texture file
zstring diffuseLODName;

// The name of the normals file matching the TNAM
zstring normalLODName;

// Always a (MUSC) form ID
formId musicId = 0;

// Water Environment Map
zstring waterEnvMapName;
};

Data GetData(CompressedFieldsCache& cache) const noexcept;
Expand Down
56 changes: 54 additions & 2 deletions libespm/src/WRLD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,60 @@ WRLD::Data WRLD::GetData(CompressedFieldsCache& cache) const noexcept
RecordHeaderAccess::IterateFields(
this,
[&](const char* type, uint32_t size, const char* data) {
if (!std::memcmp(type, "DATA", 4)) {
result.flags = *reinterpret_cast<const uint8_t*>(data);
if (!std::memcmp(type, "FULL", 4)) {
result.localNameIndex = *reinterpret_cast<const lstring*>(data);
} else if (!std::memcmp(type, "WCTR", 4)) {
std::copy_n(data, std::size(result.centerCellXY), result.centerCellXY);
} else if (!std::memcmp(type, "LTMP", 4)) {
result.interiorLightingId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "XEZN", 4)) {
result.encounterZoneId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "XLCN", 4)) {
result.exitLocationId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "CNAM", 4)) {
result.climateId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "NAM2", 4)) {
result.waterId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "NAM3", 4)) {
result.waterLODTypeId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "NAM4", 4)) {
result.waterLODHeight = *reinterpret_cast<const float*>(data);
} else if (!std::memcmp(type, "DNAM", 4)) {
std::copy_n(data, std::size(result.LandData), result.LandData);
} else if (!std::memcmp(type, "MODL", 4)) {
result.cloudModelFileName = reinterpret_cast<const char*>(data);
} else if (!std::memcmp(type, "MNAM", 4)) {
if (size == sizeof(WRLD::MapData::MapSize)) {
result.mapData.mapSize =
*reinterpret_cast<const WRLD::MapData::MapSize*>(data);
} else {
result.mapData = *reinterpret_cast<const WRLD::MapData*>(data);
}
} else if (!std::memcmp(type, "NAMA", 4)) {
result.distantLODMult = *reinterpret_cast<const float*>(data);
} else if (!std::memcmp(type, "DATA", 4)) {
result.flags = *reinterpret_cast<const WRLD::Flags*>(data);
} else if (!std::memcmp(type, "NAM0", 4)) {
std::copy_n(data, std::size(result.bottomLeftCoord),
result.bottomLeftCoord);
} else if (!std::memcmp(type, "NAM9", 4)) {
std::copy_n(data, std::size(result.topRightCoord),
result.topRightCoord);
} else if (!std::memcmp(type, "WNAM", 4)) {
result.parentWorldspaceId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "PNAM", 4)) {
result.useFlags = *reinterpret_cast<const WRLD::UseParentFlags*>(data);
} else if (!std::memcmp(type, "ONAM", 4)) {
std::copy_n(data, std::size(result.mapMarkerData),
result.mapMarkerData);
} else if (!std::memcmp(type, "TNAM", 4)) {
result.diffuseLODName = reinterpret_cast<const char*>(data);
} else if (!std::memcmp(type, "UNAM", 4)) {
result.normalLODName = reinterpret_cast<const char*>(data);
} else if (!std::memcmp(type, "ZNAM", 4)) {
result.musicId = *reinterpret_cast<const formId*>(data);
} else if (!std::memcmp(type, "XWEM", 4)) {
result.waterEnvMapName = reinterpret_cast<const char*>(data);
}
},
cache);
Expand Down

0 comments on commit 2471214

Please sign in to comment.