Nightfallstorm / SkyrimVRESL

Adds ESL support to Skyrim
GNU General Public License v3.0
9 stars 3 forks source link

Determine why this CTDs #39

Closed github-actions[bot] closed 11 months ago

github-actions[bot] commented 11 months ago

https://github.com/Nightfallstorm/SkyrimVRESL/blob/01f2523cd923ef29154dd75a2d47629b0135bfc1/src/sksevrhooks.h#L252


namespace SKSEVRHooks
{

    // code converted from skse64
    static std::unordered_map<std::uint32_t, std::uint32_t> s_savedModIndexMap;

    void SavePluginsList(SKSE::SerializationInterface* intfc)
    {
        DataHandler* dhand = DataHandler::GetSingleton();

        std::uint16_t regCount = 0;
        std::uint16_t lightCount = 0;
        std::uint16_t fileCount = 0;
        for (auto file : dhand->files) {
            if (file && file->compileIndex != 0xFF) {
                if (file->compileIndex != 0xFE) {
                    regCount++;
                } else {
                    lightCount++;
                }
            }
        }
        fileCount = regCount + lightCount;
        bool saveSuccessful = true;
        saveSuccessful &= intfc->OpenRecord('PLGN', 0);
        saveSuccessful &= intfc->WriteRecordData(&fileCount, sizeof(fileCount));

        logger::info("Saving plugin list ({} regular {} light {} total):", regCount, lightCount, fileCount);

        for (auto file : dhand->files) {
            if (file && file->compileIndex != 0xFF) {
                saveSuccessful &= intfc->WriteRecordData(&file->compileIndex, sizeof(file->compileIndex));
                if (file->compileIndex == 0xFE) {
                    saveSuccessful &= intfc->WriteRecordData(&file->smallFileCompileIndex, sizeof(file->smallFileCompileIndex));
                }

                std::uint16_t nameLen = strlen(file->fileName);
                saveSuccessful &= intfc->WriteRecordData(&nameLen, sizeof(nameLen));
                saveSuccessful &= intfc->WriteRecordData(file->fileName, nameLen);
                if (file->compileIndex != 0xFE) {
                    logger::info("\t[{}]\t{}", file->compileIndex, file->fileName);
                } else {
                    logger::info("\t[FE:{}]\t{}", file->smallFileCompileIndex, file->fileName);
                }
            }
        }
        if (!saveSuccessful) {
            logger::error("SKSE cosave failed");
        }
    }

    //FUN_180028550 + 0x144
    //Probably not necessary but kept just in case
    void LoadLightModList(SKSE::SerializationInterface* intfc, std::uint32_t version = 1)
    {
        enum LightModVersion
        {
            kVersion1 = 1,
            kVersion2 = 2
        };

        logger::info("Loading light mod list:");
        // in skse64 version is a param, but this should coincide with VR
        DataHandler* dhand = DataHandler::GetSingleton();

        char name[0x104] = { 0 };
        std::uint16_t nameLen = 0;

        std::uint16_t numSavedMods = 0;
        if (version == kVersion1) {
            intfc->ReadRecordData(&numSavedMods, sizeof(std::uint8_t));
        } else if (version == kVersion2) {
            intfc->ReadRecordData(&numSavedMods, sizeof(std::uint16_t));
        }

        for (std::uint32_t i = 0; i < numSavedMods; i++) {
            intfc->ReadRecordData(&nameLen, sizeof(nameLen));
            intfc->ReadRecordData(&name, nameLen);
            name[nameLen] = 0;

            std::uint32_t lightIndex = 0xFE000 | i;

            const TESFile* file = dhand->LookupModByName(name);
            if (file) {
                std::uint32_t newIndex = file->GetPartialIndex();
                s_savedModIndexMap[lightIndex] = newIndex;
                logger::info("\t({} -> {})\t{}", lightIndex, newIndex, name);
            } else {
                s_savedModIndexMap[lightIndex] = 0xFF;
            }
            logger::info("\t({} -> {})\t{}", lightIndex, s_savedModIndexMap[lightIndex], name);
        }
    }

    //FUN_180028550 + 0x1cb LoadModList // 2871b
    void LoadModList(SKSE::SerializationInterface* intfc)
    {
        logger::info("Loading mod list:");

        DataHandler* dhand = DataHandler::GetSingleton();

        char name[0x104] = { 0 };
        std::uint16_t nameLen = 0;

        std::uint8_t numSavedMods = 0;
        intfc->ReadRecordData(&numSavedMods, sizeof(numSavedMods));
        for (std::uint32_t i = 0; i < numSavedMods; i++) {
            intfc->ReadRecordData(&nameLen, sizeof(nameLen));
            intfc->ReadRecordData(&name, nameLen);
            name[nameLen] = 0;

            const TESFile* modInfo = dhand->LookupModByName(name);
            if (modInfo) {
                std::uint32_t newIndex = modInfo->GetPartialIndex();
                s_savedModIndexMap[i] = newIndex;
                logger::info("\t({} -> {})\t{}", i, newIndex, name);
            } else {
                s_savedModIndexMap[i] = 0xFF;
            }
        }
    }

    //This is new for skse64 and should be triggered on 'PLGN':
    //Attempt to inject at SKSEVR::FUN_180028550 + 0x30 before the old case statement.
    void LoadPluginList(SKSE::SerializationInterface* intfc)
    {
        DataHandler* dhand = DataHandler::GetSingleton();

        logger::info("Loading plugin list:");

        char name[0x104] = { 0 };
        std::uint16_t nameLen = 0;

        std::uint16_t modCount = 0;
        intfc->ReadRecordData(&modCount, sizeof(modCount));
        for (std::uint32_t i = 0; i < modCount; i++) {
            std::uint8_t modIndex = 0xFF;
            std::uint16_t lightModIndex = 0xFFFF;
            intfc->ReadRecordData(&modIndex, sizeof(modIndex));
            if (modIndex == 0xFE) {
                intfc->ReadRecordData(&lightModIndex, sizeof(lightModIndex));
            }

            intfc->ReadRecordData(&nameLen, sizeof(nameLen));
            intfc->ReadRecordData(&name, nameLen);
            name[nameLen] = 0;

            std::uint32_t newIndex = 0xFF;
            std::uint32_t oldIndex = modIndex == 0xFE ? (0xFE000 | lightModIndex) : modIndex;

            const TESFile* modInfo = dhand->LookupModByName(name);
            if (modInfo) {
                newIndex = modInfo->GetPartialIndex();
            }

            s_savedModIndexMap[oldIndex] = newIndex;

            logger::info("\t({} -> {})\t{}", oldIndex, newIndex, name);
        }
    }

    struct Core_LoadCallback_Switch : Xbyak::CodeGenerator
    {
        Core_LoadCallback_Switch(std::uintptr_t beginSwitch, std::uintptr_t endLoop, std::uintptr_t func = stl::unrestricted_cast<std::uintptr_t>(LoadPluginList))
        {
            Xbyak::Label funcLabel;
            mov(rdx, dword[rsp + 0x30]);
            cmp(rdx, 'PLGN');  // 'PLGN'
            jnz("KeepChecking");
            mov(rcx, rbx);  // LoadPluginList(intfc);
            sub(rsp, 0x20);
            call(ptr[rip + funcLabel]);
            add(rsp, 0x20);
            mov(rcx, endLoop);  // break out
            jmp(rcx);
            L("KeepChecking");
            mov(rcx, beginSwitch);  // keep switching
            jmp(rcx);
            L(funcLabel);
            dq(func);
        }
        // Install our hook at the specified address

        static inline void Install(std::uintptr_t a_base)
        {
            std::uintptr_t target{ a_base + 0x28573 };
            std::uintptr_t beginSwitch{ a_base + 0x28584 };
            std::uintptr_t endSwitch{ a_base + 0x28753 };

            auto newCompareCheck = Core_LoadCallback_Switch(beginSwitch, endSwitch);
            newCompareCheck.ready();
            int fillRange = beginSwitch - target;
            REL::safe_fill(target, REL::NOP, fillRange);
            auto& trampoline = SKSE::GetTrampoline();
            trampoline.create(newCompareCheck.getSize(), (void*)target);
            auto result = trampoline.allocate(newCompareCheck);
            logger::info("Core_LoadCallback_switch hookin {:x} to jmp to {:x} with base {:x}", target, (std::uintptr_t)result, a_base);
            trampoline.write_branch<5>(target, result);

            logger::info("Core_LoadCallback_Switch hooked at address SKSEVR::{:x}", target);
            logger::info("Core_LoadCallback_Switch hooked at offset SKSEVR::{:x}", target);
        }
    };
    bool NOPpatch = false;

    struct SKSEVRPatches
    {
        std::string name;
        std::uintptr_t offset;
        std::uint8_t readBytes[5];
        void* function;
    };

    std::vector<SKSEVRPatches> patches{
        { "SaveModList", 0x283bd, { 0xe8, 0x8e, 0xfc, 0xff, 0xff }, SavePluginsList },
        { "LoadModList", 0x2871b, { 0xe8, 0xc0, 0xf7, 0xff, 0xff }, LoadModList },
        { "LoadLightModList", 0x28694, { 0xe8, 0x77, 0xfa, 0xff, 0xff }, LoadLightModList },
    };
    void Install(std::uint32_t a_skse_version)
    {
        auto sksevr_base = reinterpret_cast<uintptr_t>(GetModuleHandleA("sksevr_1_4_15"));
        if (a_skse_version == 33554624) {  //2.0.12
            logger::info("Found patchable sksevr_1_4_15.dll version {} with base {:x}", a_skse_version, sksevr_base);
        } else {
            logger::info("Found unknown sksevr_1_4_15.dll version {} with base {:x}; not patching", a_skse_version, sksevr_base);
            return;
        }

        auto& tramp = SKSE::GetTrampoline();

        for (const auto& patch : patches) {
            logger::info("Trying to patch {} at {:x} with {:x}"sv, patch.name, sksevr_base + patch.offset, (std::uintptr_t)patch.function);
            std::uintptr_t target = (uintptr_t)(sksevr_base + patch.offset);
            const std::uint8_t* read_addr = (std::uint8_t*)target;
            if (std::memcmp((const void*)read_addr, patch.readBytes, sizeof(patch.readBytes))) {
                logger::info("{} Read code is not as expected; not patching"sv, patch.name);
                continue;
            }
            if (NOPpatch) {
                REL::safe_fill(target, REL::NOP, sizeof(patch.readBytes));  // NOP
            } else {
                SKSE::AllocTrampoline(14);
                tramp.write_call<5>(target, patch.function);
            }
            logger::info("SKSEVR {} patched"sv, patch.name);
        }
        // TODO: Determine why this CTDs
        Core_LoadCallback_Switch::Install(sksevr_base);
    }
}
github-actions[bot] commented 11 months ago

Closed in fd3ee5f46a555c367027da6c484484bc27629773