This is a DLL file that adds ASI plugin loading functionality to any game that uses any of the following libraries:
It is possible (and sometimes necessary) to load the original DLL by renaming it to <dllname>Hooked.dll, e.g. d3d12Hooked.dll.
Xidi needs no renaming: with the loader named dinput8.dll, dinput.dll or winmm.dll, put Xidi.32.dll or Xidi.64.dll next to it instead of Xidi's DLL of that name. A <dllname>Hooked.dll takes precedence.
The bink proxies (binkw32.dll, bink2w32.dll, binkw64.dll, bink2w64.dll) need the original: rename the game's binkw32.dll to binkw32Hooked.dll and put the loader in its place. vorbisFile.dll can replace the original outright. Back up any file before replacing it.
The original vorbisFile.dll can be kept as vorbisFileHooked.dll or vorbisHooked.dll. Without it, the loader's built-in vorbisfile (built from the official libvorbis 1.3.7 sources) is used, decoding with the game's vorbis.dll when the game has one.
To install it, you just need to place the DLL into the game directory. Usually, it works as dinput8.dll, but if that doesn't work, you can rename it (see the list of supported names above).
Put ASI files in the game's root directory or in the scripts, plugins, or update folder.
If configuration is necessary, the global.ini file can be placed next to the loader or in its scripts, plugins or update folder. It can be used alongside the chosen DLL, and if so, it is also possible to use the DLL name for the ini file (e.g., version.dll/version.ini). When several of these files exist, the later ones in that order win.
See an example of global.ini here.
Plugins are loaded when the game's own code starts running ([GlobalSets] DontLoadFromDllMain=1, the default), also for games wrapped in a protection stub (e.g. Origin) that unpacks the game and loads its DLLs itself. DontLoadFromDllMain=0 loads them as soon as the loader itself is loaded instead. If plugins do not load, or load at the wrong moment, [GlobalSets] DebugLog=1 writes <loader name>.log next to the loader: what it patched, which calls it ignored and why, what started plugin loading and the plugins it loaded.
All [GlobalSets] options and their defaults:
| Option | Default | |
|---|---|---|
LoadPlugins |
1 |
0 disables plugin loading entirely |
LoadFromScriptsOnly |
0 |
1 skips the loader's own folder, plugins load only from scripts, plugins and update folders |
LoadRecursively |
1 |
also loads plugins from the direct sub folders of scripts, plugins and update folders |
LoadExtraPlugins |
modloader\modloader.asi |
plugins loaded first, relative to the loader, separated by | |
DontLoadFromDllMain |
1 |
see above |
LoadFromAPI |
[module.]Function: only this call starts plugin loading. Set to GetSystemTimeAsFileTime by itself for GTA V and RDR2 |
|
UseD3D8to9 |
0 |
see d3d8to9 |
Direct3D8DisableMaximizedWindowedModeShim |
0 |
32-bit: turns off the compatibility shim Windows applies to Direct3D 8 games |
ModernUI |
1 |
0 uses standard Windows task dialogs |
DebugLog |
0 |
see above |
CxxHotReload |
0 |
see C++ snippets |
DisableCrashDumps, CrashDumpZip, CrashDumpFullMemory, CrashDumpMaxReports |
see CrashDumps |
The [FileLoader] section has OverloadFromFolder (default update), see update folder.
An ASI plugin is a DLL renamed to .asi. The loader loads it with LoadLibrary, then calls its exported InitializeASI function. Do the plugin's work there, not in DllMain: DllMain runs under the Windows loader lock, where showing a window, waiting for a thread, loading DLLs or using COM can deadlock the game. InitializeASI runs right after LoadLibrary, outside the lock (with the default DontLoadFromDllMain=1; with 0 the whole loading happens inside the loader's own DllMain).
extern "C" __declspec(dllexport) void InitializeASI()
{
// patches, hooks, settings...
}Other ASI loaders only call LoadLibrary. A plugin that should work with them too starts from DllMain when Ultimate ASI Loader is not the one loading it: the loader is then not on the call stack (it exports IsUltimateASILoader). A run-once guard covers loaders that do both:
#include <stacktrace> // C++23
static bool LoadedByUltimateASILoader()
{
for (const auto& frame : std::stacktrace::current())
{
HMODULE m = nullptr;
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCWSTR)frame.native_handle(), &m) &&
GetProcAddress(m, "IsUltimateASILoader"))
return true;
}
return false;
}
static void InitOnce() { static std::once_flag once; std::call_once(once, Init); }
extern "C" __declspec(dllexport) void InitializeASI() { InitOnce(); }
BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH && !LoadedByUltimateASILoader()) InitOnce(); // under the loader lock: keep Init simple
return TRUE;
}If the loader is on the stack because another plugin imported this one, the loader calls InitializeASI when it reaches the file in its own scan.
The demo plugins in source/plugins all follow this pattern; each is one source file (three also ship an .ini) (downloads: the demo-plugins release):
| Plugin | Shows |
|---|---|
MessageBox |
the smallest plugin: InitializeASI, and why nothing belongs in DllMain |
PluginTemplate |
a starting point for a fix: ini settings, a log file, an inline hook (safetyhook), a byte pattern search (Hooking.Patterns), the loader's update folder |
VirtualFiles |
the loader's file API: replaces game files from its ini without touching the disk |
FrameLimiter |
hooking a COM interface in any game: caps the frame rate of Direct3D 9 / 10 / 11 / 12 games |
ExeUnprotect |
makes the game's executable writable for old plugins that patch it without VirtualProtect |
Small patches can be written as plain C++ source files with the .cxx extension and placed wherever ASI plugins go (the game folder, scripts, plugins, update folders). The loader compiles them when the game starts; no compiler or SDK is needed. The same file also builds with Visual Studio as a DLL/ASI, so it can be debugged natively.
// scripts\NoIntro.cxx
#include <injector/injector.hpp>
#include <Hooking.Patterns.h>
void Init()
{
injector::WriteMemory<uint8_t>(hook::get_pattern("74 10 53 53 6A 1B"), 0xEB, true);
}Init() runs once the snippet is compiled, and Shutdown() when it is unloaded. Snippets can use injector/injector.hpp (memory writes, NOP, JMP, CALL), Hooking.Patterns.h (pattern scanning) and safetyhook.hpp (inline, mid, VMT and VM hooks), and call any function exported by the DLLs loaded in the game (declare it with extern "C"). The language is a subset of C++: see source/loader/cxx/cxxsnippets/README.md for what is supported.
examples/snippets has one example per common task: patching values and jumps, patterns in the exe or a DLL, inline hooks (partial and full replacement, x86 calling conventions), mid hooks, hooking DLL exports and Windows functions, redirecting one call, Direct3D/virtual methods, game structs, DLLs loaded later, ini settings, threads and hotkeys, hot reload. Its README lists what snippets can't do and what to use instead.
Problems never crash the game silently:
- A compile error shows the file, line and message, and the other snippets still load.
- A crash in
Init(or in the snippet's global initialization) is reported with the line that crashed. That snippet is unloaded, and its memory writes are undone. - A crash later on, for example in a hook the game calls, is written to
<loader name>.logwith the snippet's file, line and function. If aCrashDumpsfolder exists, the crash report names that line too.
[GlobalSets] CxxHotReload=1 reloads a snippet when its file is saved while the game runs (or a header in its folder changes). The edit is compiled first: if it has errors, they are shown and the running version stays. Otherwise the running version is unloaded and the new one starts. Unloading runs Shutdown(), removes its hooks and restores every byte it changed through WriteMemory, MakeNOP, MakeJMP and similar.
It is possible to install mods that replace files via the update folder, allowing you to avoid replacing original game files.
For example, if a mod replaces the file located at:
Resident Evil 5\nativePC_MT\Image\Archive\ChapterEnd11.arc
With Ultimate ASI Loader installed, you can create an update folder and place the file at:
Resident Evil 5\update\nativePC_MT\Image\Archive\ChapterEnd11.arc
To revert the game to its initial state, simply remove the update folder.
Please note that the update folder is relative to the game's executable (where the ASI loader is installed), so you need to adjust paths accordingly. For example:
\Gameface\Content\Movies\1080\GTA_SA_CREDITS_FINAL_1920x1080.mp4
Should be adjusted to:
\Gameface\Binaries\Win64\update\Content\Movies\1080\GTA_SA_CREDITS_FINAL_1920x1080.mp4
Starting with version 7.9.0, you can use this functionality for total conversions:
Two or more folders must be specified, and exist, for the selector dialog to appear. Define them inside global.ini under [FileLoader] section using the OverloadFromFolder key. Use the | symbol as a separator. If only one folder is specified and exists, it will be used to overload files, but the selector will not appear. Without an ini file, the update folder is always used if it exists. Example:
[FileLoader]
OverloadFromFolder=update | nightmareTo create a custom header, create update.txt inside update or total conversion folder and insert the custom name there.
Resident Evil 5\nightmare\update.txt:
Resident Evil 5 - Nightmare (Story mode mod)
The selector picks the first folder by itself after 10 seconds, unless you use the keyboard or the mouse. It is the loader's own window (light or dark like Windows, keyboard: arrow keys, Enter, Esc, 1-9); [GlobalSets] ModernUI=0 uses a standard Windows task dialog instead. Error messages use the same style.
The game sees the update folder merged into its own folders: files and folders that exist only in update appear in the game folder too (file listings included), and files added to or removed from update while the game runs are picked up. New files the game creates are written to the game folder, never into update.
Instead of a folder, the files can come from a zip archive in the packages folder: packages\<anything>.zip, or an archive split into parts (<name>.zip.001, <name>.zip.002, ... or .zip.1, .zip.2, ...). The top-level folder inside the archive names the update folder it provides (update\..., nightmare\...; case does not matter). If the folder also exists on disk, both are used and files in the folder win. Several archives providing the same folder are merged (the last one by name wins). Files are read directly from the archive and nothing is extracted to disk. The folder appears at <game>\update, so plugins can list and read it with the normal file functions, and GetOverloadPath returns that path. ASI plugins inside archives are not loaded. A zip-provided folder is marked [ZIP] in the selector dialog.
To get the current update path, use the ASI Loader's GetOverloadPathA or GetOverloadPathW exports from the ASI plugin.
// The loader can have any proxy name: find it by its IsUltimateASILoader export
bool (WINAPI* GetOverloadPathW)(wchar_t* out, size_t out_size) = nullptr;
HMODULE modules[1024];
DWORD needed = 0;
K32EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &needed);
for (DWORD i = 0; i < needed / sizeof(HMODULE) && i < 1024; ++i)
if (GetProcAddress(modules[i], "IsUltimateASILoader"))
GetOverloadPathW = (decltype(GetOverloadPathW))GetProcAddress(modules[i], "GetOverloadPathW");
wchar_t path[MAX_PATH * 4];
std::filesystem::path updatePath;
if (GetOverloadPathW && GetOverloadPathW(path, std::size(path)))
updatePath = path; // no update folder is active otherwiseThe 32-bit version of the ASI loader has a built-in window mode, which is enabled if you create an empty wndmode.ini in the folder with the ASI loader's DLL. It will be automatically filled with example configuration at the first run of the game. Settings are not universal and should be changed for each specific game, but usually, it works as is.
Some mods, like SkyGfx, require d3d8to9. It is also a part of the ASI loader, so to use it, create global.ini with the following content:
[GlobalSets]
UseD3D8to9=1The ASI Loader must be named d3d8.dll in order for this feature to take effect.
See an example of global.ini here.
The ASI loader can write a crash report when the game crashes. To use this feature, create a folder named CrashDumps next to the ASI loader's DLL (or next to the game executable). Each crash produces:
<game>.exe.<date>_<time>.log: a readable report. It starts with a summary of what crashed and where, and names the plugin if the crash happened inside one. Every code address is given as module+offset, with the function and source line when symbols are available, and as the address in IDA (the module at its own image base, ready for Go to address). For the crash location and the call stack it adds a byte pattern with wildcards that is unique in the module file, or the match number when no unique pattern exists, to find the place with IDA's binary search or a pattern scanner. The report also lists system information, the exception (including the type and message of C++ exceptions), return addresses found on the stack, registers, code and stack memory around the crash, the other threads, the loaded plugins and every loaded module with its version and image base.<game>.exe.<date>_<time>.dmp: the minidump. Open it in Visual Studio or WinDbg.<game>.exe.<date>_<time>.zip: the minidump and the log in one file. Attach this file when reporting a crash.
Options in [GlobalSets]:
| Option | Default | |
|---|---|---|
DisableCrashDumps |
0 |
1 turns crash reports off even if the folder exists |
CrashDumpZip |
1 |
0 skips the .zip, leaving the .log and .dmp |
CrashDumpFullMemory |
0 |
1 writes a full memory dump (large, but contains everything) |
CrashDumpMaxReports |
10 |
number of reports kept in the folder; older ones are deleted (0 keeps all) |
The report is written by a separate thread, so crashes such as stack overflows are captured too. Once installed, the game cannot replace the crash handler.
- Enable Developer Mode (Windows Settings -> Update and Security -> For Developers -> Developer Mode)

- Install a UWP game, for example, GTA San Andreas.

- Launch a UWP game through the start menu.
- Open UWPInjector.exe from the UWPDumper download.

- Enter the Process ID that is displayed from the injector and then hit enter.
- Wait until the game is dumped.

- Go to the directory :
C:\Users\[YOUR USERNAME]\AppData\Local\Packages\[YOUR UWP GAME NAME]\TempState\DUMP - Copy these files into a new folder somewhere else of your choosing.
- Uninstall a UWP game by clicking on the start menu, right-clicking on its icon, and uninstall.

- Go to your directory with your new dumped files (the ones you copied over) and shift + right-click in the directory and "Open Powershell window here".
- In that folder, rename AppxBlockMap.xml and AppxSignature.xml to anything else (to bypass UWP restrictions).
- Run the following command:
Add-AppxPackage -Register AppxManifest.xml - Place the Ultimate ASI Loader DLL into the game directory. You need to find out which name works for a specific game, in the case of GTA SA I've used d3d11.dll, so I put dinput8.dll from the x86 archive and renamed it to d3d11.dll.
- Create a scripts or plugins folder within the root directory and place your plugins in it. Rough code example of radio for all vehicles plugin here. Compiled binary here - GTASAUWP.RadioForAllVehicles.zip
- Click on the start menu and launch the game!
- See your mods in action.

