diff --git a/loader/include/Geode/loader/Loader.hpp b/loader/include/Geode/loader/Loader.hpp
index f9dd1815..bd824a82 100644
--- a/loader/include/Geode/loader/Loader.hpp
+++ b/loader/include/Geode/loader/Loader.hpp
@@ -63,6 +63,7 @@ namespace geode {
 
     public:
         // TODO: do we want to expose all of these functions?
+
         static Loader* get();
 
         enum class LoadingState : uint8_t {
@@ -76,6 +77,7 @@ namespace geode {
             Done
         };
 
+        // TODO: return void
         Result<> saveData();
         Result<> loadData();
 
diff --git a/loader/src/hooks/save.cpp b/loader/src/hooks/save.cpp
index c3dfde14..77432a9c 100644
--- a/loader/src/hooks/save.cpp
+++ b/loader/src/hooks/save.cpp
@@ -6,14 +6,18 @@ using namespace geode::prelude;
 
 struct SaveLoader : Modify<SaveLoader, AppDelegate> {
     void trySaveGame() {
-        log::info("Saving...");
+        log::info("Saving mod data...");
+        log::pushNest();
 
-        auto r = Loader::get()->saveData();
-        if (!r) {
-            log::info("{}", r.unwrapErr());
-        }
+        auto begin = std::chrono::high_resolution_clock::now();
 
-        log::info("Saved");
+        (void)Loader::get()->saveData();
+
+        auto end = std::chrono::high_resolution_clock::now();
+        auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
+        log::info("Took {}s", static_cast<float>(time) / 1000.f);
+
+        log::popNest();
 
         return AppDelegate::trySaveGame();
     }
diff --git a/loader/src/load.cpp b/loader/src/load.cpp
index 85e05d42..ba849912 100644
--- a/loader/src/load.cpp
+++ b/loader/src/load.cpp
@@ -58,8 +58,17 @@ $execute {
 }
 
 int geodeEntry(void* platformData) {
+    log::Logger::setup();
+
+    log::info("Entry");
+
+    auto begin = std::chrono::high_resolution_clock::now();
+
     // set up internal mod, settings and data
+    log::info("Setting up internal mod");
+    log::pushNest();
     auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
+    log::popNest();
     if (!internalSetupRes) {
         LoaderImpl::get()->platformMessageBox(
             "Unable to Load Geode!",
@@ -72,30 +81,38 @@ int geodeEntry(void* platformData) {
 
     // open console
     if (Mod::get()->getSettingValue<bool>("show-platform-console")) {
+        log::debug("Opening console");
         Loader::get()->openPlatformConsole();
     }
 
     // set up loader, load mods, etc.
+    log::info("Setting up loader");
+    log::pushNest();
     auto setupRes = LoaderImpl::get()->setup();
+    log::popNest();
     if (!setupRes) {
         LoaderImpl::get()->platformMessageBox(
             "Unable to Load Geode!",
             "There was an unknown fatal error setting up "
-            "the loader and Geode can not be loaded. " 
+            "the loader and Geode can not be loaded. "
             "(" + setupRes.unwrapErr() + ")"
         );
         LoaderImpl::get()->forceReset();
         return 1;
     }
 
-    log::info("Set up loader");
-
     // download and install new loader update in the background
     if (Mod::get()->getSettingValue<bool>("auto-check-updates")) {
+        log::info("Starting loader update check");
         LoaderImpl::get()->checkForLoaderUpdates();
     }
+    else {
+        log::info("Skipped loader update check");
+    }
 
-    log::debug("Entry done.");
+    auto end = std::chrono::high_resolution_clock::now();
+    auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
+    log::info("Entry took {}s", static_cast<float>(time) / 1000.f);
 
     return 0;
 }
diff --git a/loader/src/loader/HookImpl.cpp b/loader/src/loader/HookImpl.cpp
index 58be1dce..cd053ff4 100644
--- a/loader/src/loader/HookImpl.cpp
+++ b/loader/src/loader/HookImpl.cpp
@@ -44,28 +44,35 @@ tulip::hook::HookMetadata Hook::Impl::getHookMetadata() const {
 }
 
 Result<> Hook::Impl::enable() {
-    if (!m_enabled) {
-        if (!LoaderImpl::get()->hasHandler(m_address)) {
-            GEODE_UNWRAP(LoaderImpl::get()->createHandler(m_address, m_handlerMetadata));
-        }
-        GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
+    if (m_enabled)
+        return Ok();
 
-        m_handle = tulip::hook::createHook(handler, m_detour, m_hookMetadata);
-        log::debug("Enabling hook at function {} with address {}", m_displayName, m_address);
-        m_enabled = true;
+    if (!LoaderImpl::get()->hasHandler(m_address)) {
+        GEODE_UNWRAP(LoaderImpl::get()->createHandler(m_address, m_handlerMetadata));
     }
+    GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
+
+    m_handle = tulip::hook::createHook(handler, m_detour, m_hookMetadata);
+    if (m_owner)
+        log::debug("Enabled {} hook at {} for {}", m_displayName, m_address, m_owner->getID());
+    else
+        log::debug("Enabled {} hook at {}", m_displayName, m_address);
+
+    m_enabled = true;
     return Ok();
 }
 
 Result<> Hook::Impl::disable() {
-    if (m_enabled) {
-        GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
+    if (!m_enabled)
+        return Ok();
 
-        tulip::hook::removeHook(handler, m_handle);
+    GEODE_UNWRAP_INTO(auto handler, LoaderImpl::get()->getHandler(m_address));
 
-        log::debug("Disabling hook at function {}", m_displayName);
-        m_enabled = false;
-    }
+    tulip::hook::removeHook(handler, m_handle);
+
+    log::debug("Disabled {} hook", m_displayName);
+
+    m_enabled = false;
     return Ok();
 }
 
@@ -101,4 +108,4 @@ bool Hook::Impl::getAutoEnable() const {
 
 void Hook::Impl::setAutoEnable(bool autoEnable) {
     m_autoEnable = autoEnable;
-}
\ No newline at end of file
+}
diff --git a/loader/src/loader/Index.cpp b/loader/src/loader/Index.cpp
index ff1d4b1d..5cc68c1b 100644
--- a/loader/src/loader/Index.cpp
+++ b/loader/src/loader/Index.cpp
@@ -403,6 +403,8 @@ void Index::Impl::checkForUpdates() {
 
 void Index::Impl::updateFromLocalTree() {
     log::debug("Updating local index cache");
+    log::pushNest();
+
     IndexUpdateEvent(UpdateProgress(100, "Updating local cache")).post();
     // delete old items
     m_items.clear();
@@ -442,6 +444,8 @@ void Index::Impl::updateFromLocalTree() {
     // mark source as finished
     m_isUpToDate = true;
     IndexUpdateEvent(UpdateFinished()).post();
+
+    log::popNest();
 }
 
 void Index::update(bool force) {
diff --git a/loader/src/loader/LoaderImpl.cpp b/loader/src/loader/LoaderImpl.cpp
index 52727ff0..d29a1c88 100644
--- a/loader/src/loader/LoaderImpl.cpp
+++ b/loader/src/loader/LoaderImpl.cpp
@@ -61,33 +61,30 @@ Result<> Loader::Impl::setup() {
         return Ok();
     }
 
-    log::Logger::setup();
-
-    if (crashlog::setupPlatformHandler()) {
-        log::debug("Set up platform crash logger");
-    }
-    else {
-        log::debug("Unable to set up platform crash logger");
+    log::debug("Setting up crash handler");
+    log::pushNest();
+    if (!crashlog::setupPlatformHandler()) {
+        log::debug("Failed to set up crash handler");
     }
+    log::popNest();
 
-    log::debug("Setting up Loader...");
-
-    log::debug("Set up internal mod representation");
-    log::debug("Loading hooks... ");
-
+    log::debug("Loading hooks");
+    log::pushNest();
     if (!this->loadHooks()) {
         return Err("There were errors loading some hooks, see console for details");
     }
+    log::popNest();
 
-    log::debug("Loaded hooks");
-
-    log::debug("Setting up IPC...");
-
+    log::debug("Setting up IPC");
+    log::pushNest();
     this->setupIPC();
+    log::popNest();
 
+    log::debug("Setting up directories");
+    log::pushNest();
     this->createDirectories();
-
     this->addSearchPaths();
+    log::popNest();
 
     this->refreshModGraph();
 
@@ -107,14 +104,14 @@ void Loader::Impl::updateResources() {
 
 void Loader::Impl::updateResources(bool forceReload) {
     log::debug("Adding resources");
-
-    // add mods' spritesheets
+    log::pushNest();
     for (auto const& [_, mod] : m_mods) {
-        if (forceReload || !ModImpl::getImpl(mod)->m_resourcesLoaded) {
-            this->updateModResources(mod);
-            ModImpl::getImpl(mod)->m_resourcesLoaded = true;
-        }
+        if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded)
+            continue;
+        this->updateModResources(mod);
+        ModImpl::getImpl(mod)->m_resourcesLoaded = true;
     }
+    log::popNest();
 }
 
 std::vector<Mod*> Loader::Impl::getAllMods() {
@@ -164,25 +161,27 @@ bool Loader::Impl::isModVersionSupported(VersionInfo const& version) {
 // Data saving
 
 Result<> Loader::Impl::saveData() {
-    // save mods' data
     for (auto& [id, mod] : m_mods) {
+        log::debug("{}", mod->getID());
+        log::pushNest();
         auto r = mod->saveData();
         if (!r) {
             log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
         }
+        log::popNest();
     }
-    // save loader data
-    GEODE_UNWRAP(Mod::get()->saveData());
-
     return Ok();
 }
 
 Result<> Loader::Impl::loadData() {
     for (auto& [_, mod] : m_mods) {
+        log::debug("{}", mod->getID());
+        log::pushNest();
         auto r = mod->loadData();
         if (!r) {
             log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
         }
+        log::popNest();
     }
     return Ok();
 }
@@ -225,9 +224,9 @@ void Loader::Impl::updateModResources(Mod* mod) {
     if (mod->getMetadata().getSpritesheets().empty())
         return;
 
-    log::debug("Adding resources for {}", mod->getID());
+    log::debug("{}", mod->getID());
+    log::pushNest();
 
-    // add spritesheets
     for (auto const& sheet : mod->getMetadata().getSpritesheets()) {
         log::debug("Adding sheet {}", sheet);
         auto png = sheet + ".png";
@@ -246,6 +245,8 @@ void Loader::Impl::updateModResources(Mod* mod) {
             CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str());
         }
     }
+
+    log::popNest();
 }
 
 // Dependencies and refreshing
@@ -422,6 +423,9 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
             return;
         }
 
+        if (node->getID() == "absolllute.megahack")
+            log::debug("megahack creepypasta");
+
         for (auto const& dep : node->m_impl->m_dependants) {
             this->loadModGraph(dep, early);
         }
@@ -512,17 +516,17 @@ void Loader::Impl::findProblems() {
 }
 
 void Loader::Impl::refreshModGraph() {
-    log::info("Refreshing mod graph...");
+    log::info("Refreshing mod graph");
     log::pushNest();
 
-    auto begin = std::chrono::high_resolution_clock::now();
-
     if (m_isSetup) {
         log::error("Cannot refresh mod graph after startup");
         log::popNest();
         return;
     }
 
+    auto begin = std::chrono::high_resolution_clock::now();
+
     m_problems.clear();
 
     m_loadingState = LoadingState::Queue;
@@ -641,23 +645,22 @@ bool Loader::Impl::isReadyToHook() const {
     return m_readyToHook;
 }
 
-void Loader::Impl::addInternalHook(Hook* hook, Mod* mod) {
-    m_internalHooks.emplace_back(hook, mod);
+void Loader::Impl::addUninitializedHook(Hook* hook, Mod* mod) {
+    m_uninitializedHooks.emplace_back(hook, mod);
 }
 
 bool Loader::Impl::loadHooks() {
     m_readyToHook = true;
-    auto thereWereErrors = false;
-    for (auto const& hook : m_internalHooks) {
+    bool hadErrors = false;
+    for (auto const& hook : m_uninitializedHooks) {
         auto res = hook.second->addHook(hook.first);
         if (!res) {
             log::internalLog(Severity::Error, hook.second, "{}", res.unwrapErr());
-            thereWereErrors = true;
+            hadErrors = true;
         }
     }
-    // free up memory
-    m_internalHooks.clear();
-    return !thereWereErrors;
+    m_uninitializedHooks.clear();
+    return !hadErrors;
 }
 
 void Loader::Impl::queueInMainThread(ScheduledFunction func) {
@@ -1037,21 +1040,16 @@ void Loader::Impl::provideNextMod(Mod* mod) {
 }
 
 Mod* Loader::Impl::takeNextMod() {
-    if (!m_nextMod) {
-        m_nextMod = this->createInternalMod();
-        log::debug("Created internal mod {}", m_nextMod->getName());
-    }
-    auto ret = m_nextMod;
-    return ret;
+    if (!m_nextMod)
+        m_nextMod = this->getInternalMod();
+    return m_nextMod;
 }
 
 void Loader::Impl::releaseNextMod() {
     m_nextMod = nullptr;
-
     m_nextModLock.unlock();
 }
 
-
 Result<> Loader::Impl::createHandler(void* address, tulip::hook::HandlerMetadata const& metadata) {
     if (m_handlerHandles.count(address)) {
         return Err("Handler already exists at address");
diff --git a/loader/src/loader/LoaderImpl.hpp b/loader/src/loader/LoaderImpl.hpp
index 38d618eb..041b602e 100644
--- a/loader/src/loader/LoaderImpl.hpp
+++ b/loader/src/loader/LoaderImpl.hpp
@@ -72,7 +72,7 @@ namespace geode {
         std::vector<utils::MiniFunction<void(void)>> m_gdThreadQueue;
         mutable std::mutex m_gdThreadMutex;
         bool m_platformConsoleOpen = false;
-        std::vector<std::pair<Hook*, Mod*>> m_internalHooks;
+        std::vector<std::pair<Hook*, Mod*>> m_uninitializedHooks;
         bool m_readyToHook = false;
 
         std::mutex m_nextModMutex;
@@ -173,9 +173,9 @@ namespace geode {
         bool isNewUpdateDownloaded() const;
 
         bool isReadyToHook() const;
-        void addInternalHook(Hook* hook, Mod* mod);
+        void addUninitializedHook(Hook* hook, Mod* mod);
 
-        Mod* createInternalMod();
+        Mod* getInternalMod();
         Result<> setupInternalMod();
 
         bool userTriedToLoadDLLs() const;
diff --git a/loader/src/loader/Log.cpp b/loader/src/loader/Log.cpp
index ec69eed7..2b84b292 100644
--- a/loader/src/loader/Log.cpp
+++ b/loader/src/loader/Log.cpp
@@ -113,6 +113,36 @@ std::string Log::toString(bool logTime, uint32_t nestLevel) const {
         res += fmt::format("{:%H:%M:%S}", m_time);
     }
 
+    switch (m_severity.m_value) {
+        case Severity::Debug:
+            res += " DBG";
+            break;
+        case Severity::Info:
+            res += " INF";
+            break;
+        case Severity::Notice:
+            res += " NTC";
+            break;
+        case Severity::Warning:
+            res += " WRN";
+            break;
+        case Severity::Error:
+            res += " ERR";
+            break;
+        case Severity::Critical:
+            res += " CRT";
+            break;
+        case Severity::Alert:
+            res += " ALR";
+            break;
+        case Severity::Emergency:
+            res += " FAT";
+            break;
+        default:
+            res += " UNK";
+            break;
+    }
+
     res += fmt::format(" [{}]: ", m_sender ? m_sender->getName() : "Geode?");
 
     for (uint32_t i = 0; i < nestLevel; i++) {
diff --git a/loader/src/loader/ModImpl.cpp b/loader/src/loader/ModImpl.cpp
index 73849f7a..50602396 100644
--- a/loader/src/loader/ModImpl.cpp
+++ b/loader/src/loader/ModImpl.cpp
@@ -224,7 +224,7 @@ Result<> Mod::Impl::saveData() {
         log::debug("Check covered");
         for (auto& [key, value] : m_savedSettingsData.as_object()) {
             log::debug("Check if {} is saved", key);
-            if (!coveredSettings.count(key)) {
+            if (!coveredSettings.contains(key)) {
                 json[key] = value;
             }
         }
@@ -465,18 +465,21 @@ Result<> Mod::Impl::disableHook(Hook* hook) {
 }
 
 Result<Hook*> Mod::Impl::addHook(Hook* hook) {
-    m_hooks.push_back(hook);
-    if (LoaderImpl::get()->isReadyToHook()) {
-        if (this->isEnabled() && hook->getAutoEnable()) {
-            auto res = this->enableHook(hook);
-            if (!res) {
-                delete hook;
-                return Err("Can't create hook: "+ res.unwrapErr());
-            }
-        }
+    if (!ranges::contains(m_hooks, [&](auto const& h) { return h != hook; }))
+        m_hooks.push_back(hook);
+
+    if (!LoaderImpl::get()->isReadyToHook()) {
+        LoaderImpl::get()->addUninitializedHook(hook, m_self);
+        return Ok(hook);
     }
-    else {
-        LoaderImpl::get()->addInternalHook(hook, m_self);
+
+    if (!this->isEnabled() || !hook->getAutoEnable())
+        return Ok(hook);
+
+    auto res = this->enableHook(hook);
+    if (!res) {
+        delete hook;
+        return Err("Can't create hook: " + res.unwrapErr());
     }
 
     return Ok(hook);
@@ -620,9 +623,10 @@ static Result<ModMetadata> getModImplInfo() {
     return Ok(info);
 }
 
-Mod* Loader::Impl::createInternalMod() {
+Mod* Loader::Impl::getInternalMod() {
     auto& mod = Mod::sharedMod<>;
-    if (mod) return mod;
+    if (mod)
+        return mod;
     auto infoRes = getModImplInfo();
     if (!infoRes) {
         LoaderImpl::get()->platformMessageBox(
@@ -639,6 +643,7 @@ Mod* Loader::Impl::createInternalMod() {
     }
     mod->m_impl->m_enabled = true;
     m_mods.insert({ mod->getID(), mod });
+    log::debug("Created internal mod {}", mod->getName());
     return mod;
 }
 
diff --git a/loader/src/platform/windows/LoaderImpl.cpp b/loader/src/platform/windows/LoaderImpl.cpp
index df77a0a9..ff618be9 100644
--- a/loader/src/platform/windows/LoaderImpl.cpp
+++ b/loader/src/platform/windows/LoaderImpl.cpp
@@ -20,24 +20,37 @@ void Loader::Impl::platformMessageBox(char const* title, std::string const& info
 bool hasAnsiColorSupport = false;
 
 void Loader::Impl::logConsoleMessageWithSeverity(std::string const& msg, Severity severity) {
-    if (m_platformConsoleOpen) {
-        if (hasAnsiColorSupport) {
-            int color = 0;
-            switch (severity) {
-                case Severity::Debug: color = 243; break;
-                case Severity::Info: color = 33; break;
-                case Severity::Warning: color = 229; break;
-                case Severity::Error: color = 9; break;
-                default: color = 7; break;
-            }
-            auto const colorStr = fmt::format("\x1b[38;5;{}m", color);
-            auto const newMsg = fmt::format("{}{}\x1b[0m{}", colorStr, msg.substr(0, 8), msg.substr(8));
+    if (!m_platformConsoleOpen)
+        return;
 
-            std::cout << newMsg << "\n" << std::flush;
-        } else {
-            std::cout << msg << "\n" << std::flush;
-        }
+    if (!hasAnsiColorSupport) {
+        std::cout << msg << "\n" << std::flush;
+        return;
     }
+
+    int color = 0;
+    switch (severity) {
+        case Severity::Debug:
+            color = 243;
+            break;
+        case Severity::Info:
+            color = 33;
+            break;
+        case Severity::Warning:
+            color = 229;
+            break;
+        case Severity::Error:
+            color = 9;
+            break;
+        default:
+            color = 7;
+            break;
+    }
+    auto const colorStr = fmt::format("\x1b[38;5;{}m", color);
+    auto const newMsg = fmt::format("{}{}\x1b[0m{}", colorStr, msg.substr(0, 12),
+        msg.substr(12));
+
+    std::cout << newMsg << "\n" << std::flush;
 }
 
 void Loader::Impl::openPlatformConsole() {