Skip to content

Commit

Permalink
Merge pull request #115 from ryobg/devel
Browse files Browse the repository at this point in the history
skse:form_from_file for VR and ESL support
  • Loading branch information
ryobg authored Feb 27, 2024
2 parents b94be97 + c8bfb73 commit fcd6fdf
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 5 deletions.
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
image: Visual Studio 2019
version: '4.2.10.{build}'
version: '4.2.11.{build}'
platform: x64
branches:
only:
Expand Down
2 changes: 2 additions & 0 deletions src/JContainers/JContainers.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@
<ClCompile Include="src\reflection\detail\reflection.cpp" />
<ClCompile Include="src\skse\skse.cpp" />
<ClCompile Include="src\skse_callbacks.cpp" />
<ClCompile Include="src\SkyrimVRESLAPI.cpp" />
<ClCompile Include="src\testing_api.cpp" />
<ClCompile Include="src\jc_interface.cpp" />
<ClCompile Include="src\jcontainers_pch.cpp">
Expand Down Expand Up @@ -770,6 +771,7 @@
<ClInclude Include="src\skse\skse.h" />
<ClInclude Include="src\skse\papyrus_args.hpp" />
<ClInclude Include="src\skse\string.h" />
<ClInclude Include="src\SkyrimVRESLAPI.h" />
<ClInclude Include="src\typedefs.h" />
<ClInclude Include="src\util\atomic_serialization.h" />
<ClInclude Include="src\util\cstring.h" />
Expand Down
2 changes: 2 additions & 0 deletions src/JContainers/JContainers.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<ClCompile Include="src\domains\domain_master.cpp">
<Filter>domain_master</Filter>
</ClCompile>
<ClCompile Include="src\SkyrimVRESLAPI.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\gtest.h">
Expand Down Expand Up @@ -273,6 +274,7 @@
<ClInclude Include="src\boost_extras.h">
<Filter>util</Filter>
</ClInclude>
<ClInclude Include="src\SkyrimVRESLAPI.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="JContainers.rc" />
Expand Down
86 changes: 86 additions & 0 deletions src/JContainers/src/SkyrimVRESLAPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include "SkyrimVRESLAPI.h"
// Interface code based on https://github.com/adamhynek/higgs

#ifdef JC_SKSE_VR
// Stores the API after it has already been fetched
SkyrimVRESLPluginAPI::ISkyrimVRESLInterface001* g_SkyrimVRESLInterface = nullptr;

// Fetches the interface to use from SkyrimVRESL
SkyrimVRESLPluginAPI::ISkyrimVRESLInterface001* SkyrimVRESLPluginAPI::GetSkyrimVRESLInterface001(const PluginHandle& pluginHandle, SKSEMessagingInterface* messagingInterface)
{
// If the interface has already been fetched, rturn the same object
if (g_SkyrimVRESLInterface) {
return g_SkyrimVRESLInterface;
}

// Dispatch a message to get the plugin interface from SkyrimVRESL
SkyrimVRESLMessage message;
messagingInterface->Dispatch(pluginHandle, SkyrimVRESLMessage::kMessage_GetInterface, (void*)&message, sizeof(SkyrimVRESLMessage*), SkyrimVRESLPluginName);
if (!message.GetApiFunction) {
return nullptr;
}

// Fetch the API for this version of the SkyrimVRESL interface
g_SkyrimVRESLInterface = static_cast<ISkyrimVRESLInterface001*>(message.GetApiFunction(1));
return g_SkyrimVRESLInterface;
}

const ModInfo* SkyrimVRESLPluginAPI::LookupAllLoadedModByName(const char* modName)
{
DataHandler* dataHandler = DataHandler::GetSingleton();
if (dataHandler)
{
if (!g_SkyrimVRESLInterface)
{
return dataHandler->LookupLoadedModByName(modName);
}
else
{
const ModInfo* modInfo = dataHandler->LookupLoadedModByName(modName);
if (modInfo == nullptr)
{
modInfo = SkyrimVRESLPluginAPI::LookupLoadedLightModByName(modName);
}
return modInfo;
}
}
return nullptr;
}

const ModInfo* SkyrimVRESLPluginAPI::LookupLoadedLightModByName(const char* modName)
{
if (!g_SkyrimVRESLInterface)
{
DataHandler* dataHandler = DataHandler::GetSingleton();
if (dataHandler)
{
return dataHandler->LookupLoadedModByName(modName);
}
else
{
return nullptr;
}
}
else
{
const SkyrimVRESLPluginAPI::TESFileCollection* fileCollection = g_SkyrimVRESLInterface->GetCompiledFileCollection();
if (fileCollection != nullptr)
{
for (int i = 0; i < fileCollection->smallFiles.count; i++)
{
ModInfo* smallFile = nullptr;
fileCollection->smallFiles.GetNthItem(i, smallFile);
if (smallFile != nullptr)
{
int modNameLength = strlen(modName);
if (modNameLength == strlen(smallFile->name) && _strnicmp(smallFile->name, modName, modNameLength) == 0)
{
return smallFile;
}
}
}
}
return nullptr;
}
}
#endif
58 changes: 58 additions & 0 deletions src/JContainers/src/SkyrimVRESLAPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#pragma once
#include "skse64/GameReferences.h"
#include "skse64/PluginAPI.h"
#include <skse64/GameData.h>
// Interface code based on https://github.com/adamhynek/higgs

#ifdef JC_SKSE_VR
namespace SkyrimVRESLPluginAPI
{
constexpr const auto SkyrimVRESLPluginName = "SkyrimVRESL";
// A message used to fetch SkyrimVRESL's interface
struct SkyrimVRESLMessage
{
enum : uint32_t
{
kMessage_GetInterface = 0xeacb2bef
}; // Randomly generated
void* (*GetApiFunction)(unsigned int revisionNumber) = nullptr;
};

struct TESFileCollection
{
public:
// members
tArray<ModInfo*> files; // 00
tArray<ModInfo*> smallFiles; // 18
};
STATIC_ASSERT(sizeof(TESFileCollection) == 0x30);

// Returns an ISkyrimVRESLInterface001 object compatible with the API shown below
// This should only be called after SKSE sends kMessage_PostLoad to your plugin
struct ISkyrimVRESLInterface001;
ISkyrimVRESLInterface001* GetSkyrimVRESLInterface001(const PluginHandle& pluginHandle, SKSEMessagingInterface* messagingInterface);

// This object provides access to SkyrimVRESL's mod support API
struct ISkyrimVRESLInterface001
{
// Gets the SkyrimVRESL build number
virtual unsigned int GetBuildNumber() = 0;

/// @brief Get the SSE compatible TESFileCollection for SkyrimVR.
/// This should be called after kDataLoaded to ensure it's been populated.
/// @return Pointer to TESFileCollection CompiledFileCollection.
const virtual TESFileCollection* GetCompiledFileCollection() = 0;
};

// Converts the lower bits of a FormID to a full FormID depending on plugin type
static inline UInt32 GetFullFormID(const ModInfo* modInfo, UInt32 formLower)
{
// Use modIndex of 0xFE as check for light plugin to determine proper form ID composition
return (modInfo->modIndex != 0xFE) ? UInt32(modInfo->modIndex) << 24 | (formLower & 0xFFFFFF) : 0xFE000000 | (UInt32(modInfo->lightIndex) << 12) | (formLower & 0xFFF);
}

const ModInfo* LookupAllLoadedModByName(const char* modName);
const ModInfo* LookupLoadedLightModByName(const char* modName);
} // namespace SkyrimVRESLPluginAPI
extern SkyrimVRESLPluginAPI::ISkyrimVRESLInterface001* g_SkyrimVRESLInterface;
#endif
2 changes: 1 addition & 1 deletion src/JContainers/src/jcontainers_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace collections {

# define JC_API_VERSION 4
# define JC_FEATURE_VERSION 2
# define JC_PATCH_VERSION 10
# define JC_PATCH_VERSION 11

# define JC_FILE_VERSION JC_API_VERSION, JC_FEATURE_VERSION, JC_PATCH_VERSION, 0

Expand Down
41 changes: 39 additions & 2 deletions src/JContainers/src/skse/skse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#include "skse64/PluginAPI.h"
#include "skse64/PapyrusVM.h"

#ifdef JC_SKSE_VR
#include "SkyrimVRESLAPI.h"
#endif

#include "util/stl_ext.h"
#include "forms/form_handling.h"

Expand Down Expand Up @@ -123,7 +127,25 @@ struct real_api : public skse_api
std::optional<std::uint32_t> form_from_file (std::string_view const& name, std::uint32_t form) override
{
using namespace std;
#ifndef JC_SKSE_VR
#ifdef JC_SKSE_VR
if (g_SkyrimVRESLInterface)
{
if (ModInfo const* mi = SkyrimVRESLPluginAPI::LookupAllLoadedModByName (string (name).c_str ()))
{
auto retval = make_optional (SkyrimVRESLPluginAPI::GetFullFormID(mi, form));
return retval;
}
}
else
{
DataHandler* p = DataHandler::GetSingleton ();
if (ModInfo const* mi = p->LookupModByName (string (name).c_str ()))
{
auto retval = make_optional (mi->GetFormID (form));
return retval;
}
}
#else
DataHandler* p = DataHandler::GetSingleton ();
if (ModInfo const* mi = p->LookupModByName (string (name).c_str ()))
{
Expand All @@ -148,7 +170,22 @@ struct real_api : public skse_api

std::optional<std::string_view> loaded_light_mod_name (std::uint16_t i) override
{
#ifndef JC_SKSE_VR
#ifdef JC_SKSE_VR
if (g_SkyrimVRESLInterface)
{
const SkyrimVRESLPluginAPI::TESFileCollection* fileCollection = g_SkyrimVRESLInterface->GetCompiledFileCollection();
if (i < fileCollection->smallFiles.count)
{
ModInfo* smallFile = nullptr;
fileCollection->smallFiles.GetNthItem(i, smallFile);
return smallFile->name;
}
}
else
{
JC_log("WARNING: Attempted to fetch a light plugin name in VR, but VR ESL support is not present!");
}
#else
DataHandler* p = DataHandler::GetSingleton ();
if (i < p->modList.loadedCCMods.count)
return p->modList.loadedCCMods[i]->name;
Expand Down
18 changes: 17 additions & 1 deletion src/JContainers/src/skse_callbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#include "skse64/GameForms.h"
#include "skse64/GameData.h"

#ifdef JC_SKSE_VR
#include "SkyrimVRESLAPI.h"
#endif

#include "skse/skse.h"
#include "skse64/PapyrusVM.h"
#include "util/util.h"
Expand Down Expand Up @@ -211,7 +215,7 @@ namespace {
// Pitfall: since the functions registered only ONCE, we must
// preserve context pointers during ALL gaming session

// Íóæíî ïîíèìàòü, ÷òî ïîñëå ýòîé ôè÷è íèêòî ïàìÿòíèê òåáå íå ïîñòàâèò
// ����� ��������, ��� ����� ���� ���� ����� �������� ���� �� ��������

util::do_with_timing("Registering functions", [=]() {

Expand Down Expand Up @@ -269,6 +273,18 @@ namespace {
g_messaging->RegisterListener(g_pluginHandle, "SKSE", [](SKSEMessagingInterface::Message* msg) {
if (msg && msg->type == SKSEMessagingInterface::kMessage_PostPostLoad) {
g_messaging->Dispatch(g_pluginHandle, jc::message_root_interface, (void *)&jc::root, sizeof(void*), nullptr);

#ifdef JC_SKSE_VR
SkyrimVRESLPluginAPI::GetSkyrimVRESLInterface001(g_pluginHandle, g_messaging);
if (g_SkyrimVRESLInterface)
{
JC_log("SkyrimVRESL interface detected and initialized!");
}
else
{
JC_log("SkyrimVRESL interface is not present or has failed to be retrieved... ESL related functionality is disabled.");
}
#endif
}
});
}
Expand Down

0 comments on commit fcd6fdf

Please sign in to comment.