Contient un fichier d'en-tête et une base de données pour rendre la version des plugins SKSE DLL indépendante facilement.
IMPORTANT!Cette version est désormais divisée en deux : Édition spéciale (1.5.x) et Édition anniversaire (1.6.x). Les identifiants pointant vers les adresses ne correspondent pas entre ces deux versions (l'exécutable du jeu est trop différent pour correspondre, et même s'ils correspondent, le code de ces fonctions est de toute façon différent).DescriptionPour les utilisateurs réguliers du mod :Téléchargez et installez le package « tout-en-un » depuis la section « Fichiers ». Vous pouvez utiliser le gestionnaire de mods ou le faire manuellement. Les fichiers .bin devraient se trouver ici :
Données/SKSE/Plugins/Il n’est pas nécessaire que vous lisiez le reste de tout cela.
Pour les auteurs de plugins DLL SKSE :Il s'agit d'une ressource de moddeur (un fichier d'en-tête). Vous pouvez charger une base de données stockant les décalages afin que votre plugin DLL soit indépendant de la version sans nécessiter de recompilation. Le fichier d'en-tête est téléchargeable depuis la section optionnelle des fichiers. Pour l'édition anniversaire, le fichier d'en-tête s'appelle versionlibdb.h au lieu de versiondb.h ! Si vous utilisez CommonLib, tout cela est déjà intégré et vous n'avez besoin de rien d'autre.
Comment utiliserLe moyen le plus rapide :
Becquet:
Montrer
#include "versiondb.h"
void * MonAdresse = NULL;
unsigned long long MyOffset = 0;
bool InitializeOffsets()
{
// Allouer sur la pile afin qu'elle soit déchargée lorsque nous quittons cette fonction.
// Il n'est pas nécessaire de charger toute la base de données et d'utiliser de la mémoire sans raison.
VersionDb db;
// Charger la base de données avec la version exécutable actuelle.
si (!db.Load())
{
_FATALERROR("Échec du chargement de la base de données des versions de l'exécutable actuel !");
renvoie faux ;
}
autre
{
// "SkyrimSE.exe", "1.5.97.0"
_MESSAGE("Base de données chargée pour %s version %s.", db.GetModuleName().c_str(), db.GetLoadedVersionString().c_str());
}
// Cette adresse inclut déjà l'adresse de base du module, nous pouvons donc utiliser l'adresse directement.
MonAdresse = db.FindAddressById(123);
si (MonAdresse == NULL)
{
_FATALERROR("Impossible de trouver l'adresse !");
renvoie faux ;
}
// Ce décalage n'inclut pas l'adresse de base. L'adresse réelle serait ModuleBase + MyOffset.
si (!db.FindOffsetById(123, MyOffset))
{
_FATALERROR("Impossible de trouver le décalage pour mon truc !");
renvoie faux ;
}
// Tout a réussi.
renvoie vrai ;
}
Vous vous demandez maintenant à quoi correspond cette valeur « 123 ». Il s'agit de l'identifiant d'une adresse. Différentes versions de bases de données auront le même identifiant pour une adresse, mais celui-ci peut pointer vers des valeurs différentes. Pour obtenir la liste de toutes les paires identifiant/valeur d'une version spécifique, procédez comme suit :
Becquet:
Montrer
#include "versiondb.h"
booléen DumpSpecificVersion()
{
VersionDb db;
// Essayez de charger la base de données de la version 1.5.62.0 quelle que soit la version exécutable en cours d'exécution.
si (!db.Load(1, 5, 62, 0))
{
_FATALERROR("Échec du chargement de la base de données pour 1.5.62.0 !");
renvoie faux ;
}
// Écrivez un fichier appelé offsets-1.5.62.0.txt où chaque ligne est l'ID et le décalage.
db.Dump("offsets-1.5.62.0.txt");
_MESSAGE("Décalages vidés pour 1.5.62.0");
renvoie vrai ;
}
Au lieu de 1, 5, 62 ou 0, indiquez la version que vous souhaitez inverser et que vous connaissez. Vous devez d'abord avoir le fichier de base de données correspondant dans le répertoire /Data/SKSE/Plugins.
Après avoir appelé cette commande, vous devriez avoir un nouveau fichier dans le répertoire principal de Skyrim, nommé « offsets-1.5.62.0.txt », ou le nom que vous aurez choisi. Il sera au format suivant : chaque ligne sera :
ID décimal
Décalage hexadécimal
Par exemple, si vous avez une adresse 142F4DEF8 (pointeur statique du personnage du joueur) dans la version 1.5.62.0 que vous souhaitez rendre indépendante de la version, vous feriez ceci :
1. Recherchez 2F4DEF8 dans le fichier de décalages. Il s'agit du décalage sans la base 140000000.
2. Vérifiez que l'ID est 517014 (décimal !)
3. Si vous souhaitez que cette adresse soit présente dans votre DLL lors de l'exécution, procédez comme suit :
void* addressOf142F4DEF8 = db.FindAddressById(517014);
Et voilà.
La structure VersionDb a les fonctions suivantes :
Becquet:
Montrer
bool Dump(const std::string& path); // Vider la base de données actuellement chargée dans un fichier
bool Load(int major, int minor, int revision, int build); // Charger une version spécifique si le db-major-minor-revision-build.bin existe dans le répertoire Data/SKSE/Plugins
bool Load(); // Charger la version de l'application actuelle
void Clear(); // Effacer la base de données actuellement chargée
void GetLoadedVersion(int& major, int& minor, int& revision, int& build) const; // Obtenir la version du fichier de base de données que nous avons chargé en ce moment
bool GetExecutableVersion(int& major, int& minor, int& revision, int& build) const; // Obtenir la version de l'application en cours d'exécution
const std::string& GetModuleName() const; // Obtenir le nom du module de base de données actuellement chargé, cela devrait afficher « SkyrimSE.exe »
const std::string& GetLoadedVersionString() const; // Récupère la version actuellement chargée sous forme de chaîne, par exemple « 1.5.62.0 »
const std::map& GetOffsetMap() const; // Récupérez la carte d'ID à décaler si vous devez l'itérer manuellement
void* FindAddressById(unsigned long long id) const; // Rechercher une adresse par ID, cela inclura déjà la base et sera l'adresse correcte. Il renverra NULL si introuvable !
bool FindOffsetById(unsigned long long id, unsigned long long& result) const; // Rechercher le décalage par ID, ce sera juste un décalage sans base incluse.
bool FindIdByAddress(void* ptr, unsigned long long& result) const; // Rechercher l'ID par adresse, cela tentera une recherche inversée pour convertir l'adresse en ID
bool FindIdByOffset(unsigned long long offset, unsigned long long& result) const; // Rechercher l'ID par décalage, cela tentera une recherche inversée pour convertir le décalage en ID
Ce que vous devez savoir et garder à l’esprit :
1. Vous pouvez inclure tout ou partie des fichiers de base de données avec votre plugin, mais cela peut augmenter considérablement la taille du fichier (environ 2,5 Mo). Jusqu'à présent, il était courant de marquer ce mod comme une dépendance.
2. Vous devez TOUJOURS charger la base de données une seule fois au démarrage, initialiser/mettre en cache les adresses nécessaires et la laisser se décharger. Le déchargement entraîne simplement la suppression ou la perte de la structure VersionDb (si vous l'avez allouée sur la pile). Cela vous évitera d'utiliser inutilement de la mémoire pendant l'exécution du jeu. Il n'est pas nécessaire de maintenir la base de données chargée pendant le jeu. Ce point est discutable si vous utilisez CommonLib, car elle ne la charge qu'une seule fois au lieu de charger pour chaque DLL.
3. La base de données contient les adresses des fonctions, des variables globales, des RTTI, des vtables et tout autre élément susceptible d'y faire référence. Elle ne contient pas d'adresses situées au milieu des fonctions ou des variables globales. Si vous avez besoin d'une adresse au milieu d'une fonction, recherchez l'adresse de base de la fonction et ajoutez vous-même le décalage supplémentaire. Elle ne contient pas non plus d'éléments inutiles tels que l'alignement autour des fonctions (référencé dans rdata). La section pdata est ignorée et certaines informations SEH générées par le compilateur à partir de rdata sont ignorées.
4. Vérifiez toujours le résultat pour vous assurer que la base de données a bien été chargée (la commande booléenne Load a renvoyé true) et que les adresses interrogées ont bien renvoyé un résultat valide (non nul). Si le chargement échoue, cela signifie que le fichier manquait probablement une version ou qu'il était incorrect (par exemple, en essayant d'utiliser l'en-tête SE dans AE). Si la requête échoue, cela signifie que l'adresse est introuvable dans cette version. Cela peut signifier que le code du jeu a suffisamment changé pour que l'adresse ne soit plus valide pour cette version, ou que la base de données elle-même n'a pas détecté l'adresse correcte. Dans l'un ou l'autre de ces cas, vous devez annuler l'initialisation du plugin pour signaler à SKSE que le chargement a échoué. Vous pouvez également afficher manuellement un message d'erreur.
5. Il est également conseillé de vérifier que l'adresse existe dans toutes les versions du jeu avant de publier votre plugin DLL. Pour cela, chargez chaque version du fichier de base de données et interrogez le même identifiant d'adresse dans chacune d'elles pour vous assurer qu'il existe :
Becquet:
Montrer
bool LoadAll(std::vector& tous)
{
versions statiques int[] = { 3, 16, 23, 39, 50, 53, 62, 73, 80, 97, -1 };
pour (int i = 0; versions[i] >= 0; i++)
{
VersionDb * db = nouvelle VersionDb();
si (!db->Load(1, 5, versions[i], 0))
{
supprimer la base de données ;
renvoie faux ;
}
tout.push_back(db);
}
renvoie vrai ;
}
bool ExistsInAll(std::vector& tous, unsigned long long id)
{
unsigned long long résultat = 0 ;
pour (auto db : tous)
{
si (!db->FindOffsetById(id, résultat))
renvoie faux ;
}
renvoie vrai ;
}
void FreeAll(std::vector& tous)
{
pour (auto db : tous)
supprimer la base de données ;
tout.effacer();
}
booléen IsOk()
{
std::vectortous;
si (!LoadAll(tous))
{
_FATALERROR("Échec du chargement d'une ou plusieurs bases de données de versions pour l'exécutable actuel !");
FreeAll(tous);
renvoie faux ;
}
si (!ExistsInAll(tous, 517014))
{
_FATALERROR("517014 n'existe pas dans toutes les versions de la base de données !");
FreeAll(tous);
renvoie faux ;
}
FreeAll(tous);
// D'accord!
renvoie vrai ;
}
De cette façon, vous pouvez être sûr que votre mod DLL fonctionnera dans toutes les versions, ou s'il ne fonctionne pas dans certaines versions, vous pouvez l'écrire sur votre page de mod.
6. Il peut être nécessaire d'effectuer une action différente selon la version du jeu. Cet extrait de code vous permet de le faire :
Becquet:
Montrer
int majeur = 0, mineur = 0, révision = 0, build = 0 ;
si (!db.GetExecutableVersion(majeur, mineur, révision, build))
{
_FATALERROR("Quelque chose s'est mal passé !");
renvoie faux ;
}
// Le jeu en cours d'exécution est 1.5.x et au moins la version 1.5.39.0
si (majeur == 1 && mineur == 5 && révision >= 39)
{
// Truc ... ?
}
7. Veuillez noter que si vous compilez votre DLL SKSE en mode débogage, le temps de chargement de la base de données peut atteindre environ 14 secondes ! En mode publication, ce temps est d'environ 0,2 seconde. Ceci est dû à la lenteur des conteneurs de la bibliothèque standard dans ce mode (std map).
Autorisations
Fais ce que tu veux.