1
artifact-tracker/Source/ArtifactTrackerDLL/src/BookCheck.cpp

145 lines
3.6 KiB
C++
Raw Normal View History

2022-06-21 21:51:57 +00:00
#include "BookCheck.h"
// Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005
namespace BookCheck
{
2022-06-21 23:30:45 +00:00
std::unordered_map<RE::FormID, RE::TESObjectBOOK*> validBooks;
2022-06-21 21:51:57 +00:00
void PreloadBookList()
{
const auto dataHandler = RE::TESDataHandler::GetSingleton();
if (!dataHandler) {
return;
}
2022-06-22 00:52:12 +00:00
const RE::BGSKeyword* recipeKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xF5CB0, "Skyrim.esm"); // VendorItemRecipe
2022-06-21 21:51:57 +00:00
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectBOOK>()) {
2022-06-22 00:52:12 +00:00
if (!form || !form->GetPlayable() || form->TeachesSpell()) {
2022-06-21 21:51:57 +00:00
continue;
}
if (form->HasKeyword(recipeKeyword) || IsBook(form)) {
2022-06-21 23:30:45 +00:00
validBooks[form->formID] = form;
2022-06-21 21:51:57 +00:00
}
}
}
2022-06-21 23:30:45 +00:00
std::unordered_map<RE::FormID, RE::TESObjectBOOK*> GetBookList()
2022-06-21 21:51:57 +00:00
{
return validBooks;
}
const char* ExtractFileName(const char* const path, size_t& fileNameLength)
{
if (!path) {
fileNameLength = 0;
return NULL;
}
const char* ptr = path;
const char* fileNameStart = NULL;
while (*ptr != 0) {
char value = *ptr;
if (value == '\\' || value == '/') {
fileNameStart = ptr + 1;
}
ptr++;
}
const char* result = fileNameStart ? fileNameStart : path;
fileNameLength = ptr - result;
return result;
}
// Performs a case-sensitive search for 'needle' in the specified string,
// excluding any matches that are part of a larger word.
bool ContainsWholeWord(const char* const haystack, const size_t haystackLength,
const char* const needle, const size_t needleLength)
{
if (haystackLength >= needleLength) {
for (size_t i = 0; i < haystackLength; i++) {
if (haystack[i] != needle[0]) {
continue;
}
size_t j = i + 1;
size_t k = 1;
while (j < haystackLength && k < needleLength && haystack[j] == needle[k]) {
j++;
k++;
}
if (k == needleLength) {
// Check that the match is not part of a larger word.
char next = haystack[j];
return !(next >= 'a' && next <= 'z');
}
}
}
return false;
}
bool StartsWith(const char* haystack, size_t haystackLength,
const char* needle, size_t needleLength)
{
return haystackLength >= needleLength && memcmp(haystack, needle, needleLength) == 0;
}
bool IsNote(const char* const modelFileName, size_t modelFileNameLength)
{
// Most items will use one of the note models from the base game, e.g. Note01.nif.
if (ContainsWholeWord(modelFileName, modelFileNameLength, "Note", 4)) {
return true;
}
// The Creation Club Fishing mod does not include 'Note' the inventory art model names for the
// regional fishing maps, instead those items use the following format: FishMap<region>.
if (StartsWith(modelFileName, modelFileNameLength, "FishMap", 7)) {
return true;
}
return false;
}
bool IsJournal(const char* const modelFileName, size_t modelFileNameLength)
{
return ContainsWholeWord(modelFileName, modelFileNameLength, "Journal", 7);
}
bool IsBook(RE::TESObjectBOOK* book)
{
if (!book || !book->inventoryModel) {
return false;
}
RE::BGSSoundDescriptorForm* pickupSound = book->As<RE::BGSPickupPutdownSounds>()->pickupSound;
if (pickupSound && pickupSound->formID == 0xC7A54) { // ITMNoteUp
return false;
}
size_t modelFileNameLength;
const char* const modelFileName = BookCheck::ExtractFileName(book->inventoryModel->GetModel(), modelFileNameLength);
if (modelFileNameLength == 0) {
return false;
}
if (IsNote(modelFileName, modelFileNameLength) || IsJournal(modelFileName, modelFileNameLength)) {
return false;
}
return true;
}
}