fix(runtime): rework atlas merging and texture pack hooks

This commit is contained in:
Jacobwasbeast
2026-03-10 04:33:12 -05:00
parent 9118ce6682
commit 480abcafd2
7 changed files with 1020 additions and 120 deletions

View File

@@ -15,8 +15,10 @@
#include <string>
#include <cstdio>
#include <cstring>
#include <cwchar>
#include <cwctype>
#include <memory>
#include <unordered_map>
#include <cstddef>
#include <vector>
@@ -33,6 +35,7 @@ namespace GameHooks
GetString_fn Original_GetString = nullptr;
GetResourceAsStream_fn Original_GetResourceAsStream = nullptr;
LoadUVs_fn Original_LoadUVs = nullptr;
PreStitchedTextureMapStitch_fn Original_PreStitchedTextureMapStitch = nullptr;
RegisterIcon_fn Original_RegisterIcon = nullptr;
ItemInstanceGetIcon_fn Original_ItemInstanceGetIcon = nullptr;
EntityRendererBindTextureResource_fn Original_EntityRendererBindTextureResource = nullptr;
@@ -83,10 +86,17 @@ namespace GameHooks
TexturesBindTextureResource_fn Original_TexturesBindTextureResource = nullptr;
TexturesLoadTextureByName_fn Original_TexturesLoadTextureByName = nullptr;
TexturesLoadTextureByIndex_fn Original_TexturesLoadTextureByIndex = nullptr;
TexturesReadImage_fn Original_TexturesReadImage = nullptr;
StitchedTextureUV_fn Original_StitchedGetU0 = nullptr;
StitchedTextureUV_fn Original_StitchedGetU1 = nullptr;
StitchedTextureUV_fn Original_StitchedGetV0 = nullptr;
StitchedTextureUV_fn Original_StitchedGetV1 = nullptr;
BufferedImageCtorFile_fn Original_BufferedImageCtorFile = nullptr;
BufferedImageCtorDLCPack_fn Original_BufferedImageCtorDLCPack = nullptr;
TextureManagerCreateTexture_fn Original_TextureManagerCreateTexture = nullptr;
TextureTransferFromImage_fn Original_TextureTransferFromImage = nullptr;
TexturePackGetImageResource_fn Original_AbstractTexturePackGetImageResource = nullptr;
TexturePackGetImageResource_fn Original_DLCTexturePackGetImageResource = nullptr;
static int s_itemMineBlockHookCalls = 0;
static void* s_currentLevel = nullptr;
static thread_local void* s_activeUseLevel = nullptr;
@@ -123,6 +133,7 @@ namespace GameHooks
static thread_local bool s_hasForcedBillboardRoute = false;
static thread_local int s_forcedBillboardAtlas = -1;
static thread_local int s_forcedBillboardPage = 0;
static thread_local int s_activeStitchAtlasType = -1;
static int s_animatedTextureGuardLogCount = 0;
struct TextureNameArrayNative
@@ -315,6 +326,14 @@ namespace GameHooks
return lower;
}
static bool EndsWithPath(const std::wstring& path, const wchar_t* suffix)
{
size_t pathLen = path.size();
size_t suffLen = wcslen(suffix);
if (suffLen > pathLen) return false;
return path.compare(pathLen - suffLen, suffLen, suffix) == 0;
}
static int DetectAtlasTypeFromResource(void* resourcePtr)
{
if (!resourcePtr)
@@ -1341,9 +1360,23 @@ namespace GameHooks
return Original_HalfSlabCloneTileId ? Original_HalfSlabCloneTileId(thisPtr, level, x, y, z) : TryReadTileId(thisPtr);
}
void __fastcall Hooked_PreStitchedTextureMapStitch(void* thisPtr)
{
const int prevAtlasType = s_activeStitchAtlasType;
int iconType = -1;
if (thisPtr && IsReadableRange(static_cast<const char*>(thisPtr) + 8, sizeof(int)))
iconType = *reinterpret_cast<const int*>(static_cast<const char*>(thisPtr) + 8);
s_activeStitchAtlasType = iconType;
if (Original_PreStitchedTextureMapStitch)
Original_PreStitchedTextureMapStitch(thisPtr);
s_activeStitchAtlasType = prevAtlasType;
}
void __fastcall Hooked_LoadUVs(void* thisPtr)
{
LogUtil::Log("[WeaveLoader] Hooked_LoadUVs: ENTER (textureMap=%p)", thisPtr);
if (thisPtr && IsReadableRange(static_cast<const char*>(thisPtr) + 8, sizeof(int)))
s_activeStitchAtlasType = *reinterpret_cast<const int*>(static_cast<const char*>(thisPtr) + 8);
if (Original_LoadUVs)
Original_LoadUVs(thisPtr);
LogUtil::Log("[WeaveLoader] Hooked_LoadUVs: original returned, creating mod icons");
@@ -1352,40 +1385,73 @@ namespace GameHooks
LogUtil::Log("[WeaveLoader] Hooked_LoadUVs: DONE");
}
static float ApplyAtlasScale(void* iconPtr, float value, bool isU, bool isModIcon)
{
if (isModIcon)
return value;
int atlasType = -1;
if (!ModAtlas::GetIconAtlasType(iconPtr, atlasType))
{
if (s_activeStitchAtlasType >= 0)
atlasType = s_activeStitchAtlasType;
else
return value;
}
float uScale = 1.0f;
float vScale = 1.0f;
if (!ModAtlas::GetAtlasScale(atlasType, uScale, vScale))
return value;
const float scale = isU ? uScale : vScale;
if (scale > 0.9995f && scale < 1.0005f)
return value;
return value * scale;
}
float __fastcall Hooked_StitchedGetU0(void* thisPtr, bool adjust)
{
int atlasType = -1;
int page = 0;
if (ModAtlas::TryGetIconRoute(thisPtr, atlasType, page) && atlasType == 0)
const bool isModIcon = ModAtlas::TryGetIconRoute(thisPtr, atlasType, page);
if (isModIcon && atlasType == 0 && page > 0)
ModAtlas::NotifyIconSampled(thisPtr);
return Original_StitchedGetU0 ? Original_StitchedGetU0(thisPtr, adjust) : 0.0f;
float value = Original_StitchedGetU0 ? Original_StitchedGetU0(thisPtr, adjust) : 0.0f;
return ApplyAtlasScale(thisPtr, value, true, isModIcon);
}
float __fastcall Hooked_StitchedGetU1(void* thisPtr, bool adjust)
{
int atlasType = -1;
int page = 0;
if (ModAtlas::TryGetIconRoute(thisPtr, atlasType, page) && atlasType == 0)
const bool isModIcon = ModAtlas::TryGetIconRoute(thisPtr, atlasType, page);
if (isModIcon && atlasType == 0 && page > 0)
ModAtlas::NotifyIconSampled(thisPtr);
return Original_StitchedGetU1 ? Original_StitchedGetU1(thisPtr, adjust) : 0.0f;
float value = Original_StitchedGetU1 ? Original_StitchedGetU1(thisPtr, adjust) : 0.0f;
return ApplyAtlasScale(thisPtr, value, true, isModIcon);
}
float __fastcall Hooked_StitchedGetV0(void* thisPtr, bool adjust)
{
int atlasType = -1;
int page = 0;
if (ModAtlas::TryGetIconRoute(thisPtr, atlasType, page) && atlasType == 0)
const bool isModIcon = ModAtlas::TryGetIconRoute(thisPtr, atlasType, page);
if (isModIcon && atlasType == 0 && page > 0)
ModAtlas::NotifyIconSampled(thisPtr);
return Original_StitchedGetV0 ? Original_StitchedGetV0(thisPtr, adjust) : 0.0f;
float value = Original_StitchedGetV0 ? Original_StitchedGetV0(thisPtr, adjust) : 0.0f;
return ApplyAtlasScale(thisPtr, value, false, isModIcon);
}
float __fastcall Hooked_StitchedGetV1(void* thisPtr, bool adjust)
{
int atlasType = -1;
int page = 0;
if (ModAtlas::TryGetIconRoute(thisPtr, atlasType, page) && atlasType == 0)
const bool isModIcon = ModAtlas::TryGetIconRoute(thisPtr, atlasType, page);
if (isModIcon && atlasType == 0 && page > 0)
ModAtlas::NotifyIconSampled(thisPtr);
return Original_StitchedGetV1 ? Original_StitchedGetV1(thisPtr, adjust) : 0.0f;
float value = Original_StitchedGetV1 ? Original_StitchedGetV1(thisPtr, adjust) : 0.0f;
return ApplyAtlasScale(thisPtr, value, false, isModIcon);
}
void __fastcall Hooked_TexturesBindTextureResource(void* thisPtr, void* resourcePtr)
@@ -1498,19 +1564,152 @@ namespace GameHooks
return Original_TexturesLoadTextureByIndex(thisPtr, idx);
}
static bool IsModLoaderGeneratedPath(const std::wstring& lower)
{
return (lower.find(L"/mods/modloader/generated/") != std::wstring::npos) ||
(lower.find(L"/modloader/") != std::wstring::npos);
}
static std::wstring ToLowerSimple(const std::wstring& s)
{
std::wstring lower;
lower.reserve(s.size());
for (wchar_t ch : s)
lower.push_back((wchar_t)towlower(ch));
return lower;
}
static std::unordered_map<void*, std::wstring> s_textureNames;
void* __fastcall Hooked_TexturesReadImage(void* thisPtr, int texId, const std::wstring& name)
{
if (!Original_TexturesReadImage)
return nullptr;
void* img = Original_TexturesReadImage(thisPtr, texId, name);
if (!img)
return img;
std::wstring lower = NormalizeLowerPath(name);
if (IsModLoaderGeneratedPath(lower))
return img;
if (EndsWithPath(lower, L"terrain.png"))
{
ModAtlas::OverrideAtlasFromBufferedImage(0, img);
}
else if (EndsWithPath(lower, L"items.png"))
{
ModAtlas::OverrideAtlasFromBufferedImage(1, img);
}
return img;
}
void __fastcall Hooked_BufferedImageCtorFile(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive)
{
if (Original_BufferedImageCtorFile)
Original_BufferedImageCtorFile(thisPtr, file, filenameHasExtension, bTitleUpdateTexture, drive);
// Intentionally left empty: handled in Textures::readImage hook to avoid
// constructor-level crashes during boot.
}
void __fastcall Hooked_BufferedImageCtorDLCPack(void* thisPtr, void* dlcPack, const std::wstring& file, bool filenameHasExtension)
{
if (Original_BufferedImageCtorDLCPack)
Original_BufferedImageCtorDLCPack(thisPtr, dlcPack, file, filenameHasExtension);
// Intentionally left empty: handled in Textures::readImage hook to avoid
// constructor-level crashes during boot.
}
void* __fastcall Hooked_TextureManagerCreateTexture(void* thisPtr, const std::wstring& name, int mode, int width, int height, int wrap, int format, int minFilter, int magFilter, bool mipmap, void* image)
{
if (!Original_TextureManagerCreateTexture)
return nullptr;
void* tex = Original_TextureManagerCreateTexture(thisPtr, name, mode, width, height, wrap, format, minFilter, magFilter, mipmap, image);
if (tex)
{
std::wstring lower = ToLowerSimple(name);
if (lower == L"terrain" || lower == L"items")
s_textureNames[tex] = lower;
}
return tex;
}
void __fastcall Hooked_TextureTransferFromImage(void* thisPtr, void* image)
{
if (!Original_TextureTransferFromImage)
return;
auto it = s_textureNames.find(thisPtr);
if (it != s_textureNames.end() && image)
{
if (it->second == L"terrain")
ModAtlas::OverrideAtlasFromBufferedImage(0, image);
else if (it->second == L"items")
ModAtlas::OverrideAtlasFromBufferedImage(1, image);
}
Original_TextureTransferFromImage(thisPtr, image);
}
static void TryOverrideAtlasFromPackImage(const std::wstring& file, void* image)
{
if (!image) return;
std::wstring lower = NormalizeLowerPath(file);
if (IsModLoaderGeneratedPath(lower))
return;
if (EndsWithPath(lower, L"terrain.png"))
{
ModAtlas::OverrideAtlasFromBufferedImage(0, image);
}
else if (EndsWithPath(lower, L"items.png"))
{
ModAtlas::OverrideAtlasFromBufferedImage(1, image);
}
}
void* __fastcall Hooked_AbstractTexturePackGetImageResource(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive)
{
if (!Original_AbstractTexturePackGetImageResource)
return nullptr;
void* img = Original_AbstractTexturePackGetImageResource(thisPtr, file, filenameHasExtension, bTitleUpdateTexture, drive);
TryOverrideAtlasFromPackImage(file, img);
return img;
}
void* __fastcall Hooked_DLCTexturePackGetImageResource(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive)
{
if (!Original_DLCTexturePackGetImageResource)
return nullptr;
void* img = Original_DLCTexturePackGetImageResource(thisPtr, file, filenameHasExtension, bTitleUpdateTexture, drive);
TryOverrideAtlasFromPackImage(file, img);
return img;
}
static int s_registerIconCallCount = 0;
void* __fastcall Hooked_RegisterIcon(void* thisPtr, const std::wstring& name)
{
s_registerIconCallCount++;
int iconType = -1;
if (thisPtr && IsReadableRange(static_cast<const char*>(thisPtr) + 8, sizeof(int)))
iconType = *reinterpret_cast<const int*>(static_cast<const char*>(thisPtr) + 8);
void* modIcon = ModAtlas::LookupModIcon(name);
if (modIcon)
{
if (iconType >= 0)
ModAtlas::NoteIconAtlasType(modIcon, iconType);
LogUtil::Log("[WeaveLoader] registerIcon #%d: '%ls' -> MOD ICON %p",
s_registerIconCallCount, name.c_str(), modIcon);
return modIcon;
}
void* result = Original_RegisterIcon ? Original_RegisterIcon(thisPtr, name) : nullptr;
if (result && iconType >= 0)
ModAtlas::NoteIconAtlasType(result, iconType);
if (s_registerIconCallCount <= 30 || !result)
{
LogUtil::Log("[WeaveLoader] registerIcon #%d: '%ls' -> vanilla %p",
@@ -2034,37 +2233,58 @@ namespace GameHooks
void* Hooked_GetResourceAsStream(const void* fileName)
{
const std::wstring* path = static_cast<const std::wstring*>(fileName);
if (ModAtlas::HasModTextures() && Original_GetResourceAsStream && path)
if (!Original_GetResourceAsStream || !path)
return Original_GetResourceAsStream ? Original_GetResourceAsStream(fileName) : nullptr;
int atlasType = -1;
int page = 0;
if (ParseVirtualAtlasRequest(*path, atlasType, page))
{
std::string terrainPath = ModAtlas::GetMergedTerrainPath();
std::string itemsPath = ModAtlas::GetMergedItemsPath();
if (!terrainPath.empty() && path->find(L"terrain.png") != std::wstring::npos)
std::string atlasPath = ModAtlas::GetVirtualPagePath(atlasType, page);
if (atlasPath.empty())
atlasPath = ModAtlas::GetMergedPagePath(atlasType, page);
if (!atlasPath.empty())
{
std::wstring ourPath(terrainPath.begin(), terrainPath.end());
LogUtil::Log("[WeaveLoader] getResourceAsStream: redirecting terrain.png to merged atlas");
std::wstring ourPath(atlasPath.begin(), atlasPath.end());
return Original_GetResourceAsStream(&ourPath);
}
if (!itemsPath.empty() && path->find(L"items.png") != std::wstring::npos)
}
std::wstring lower = NormalizeLowerPath(*path);
const bool isGenerated =
(lower.find(L"/modloader/") != std::wstring::npos) ||
(lower.find(L"/mods/modloader/generated/") != std::wstring::npos);
if (!isGenerated)
{
if (EndsWithPath(lower, L"terrain.png"))
{
std::wstring ourPath(itemsPath.begin(), itemsPath.end());
LogUtil::Log("[WeaveLoader] getResourceAsStream: redirecting items.png to merged atlas");
return Original_GetResourceAsStream(&ourPath);
}
int atlasType = -1;
int page = 0;
if (ParseVirtualAtlasRequest(*path, atlasType, page))
{
std::string atlasPath = ModAtlas::GetVirtualPagePath(atlasType, page);
if (atlasPath.empty())
atlasPath = ModAtlas::GetMergedPagePath(atlasType, page);
if (!atlasPath.empty())
ModAtlas::SetOverrideAtlasPath(0, std::string(path->begin(), path->end()));
ModAtlas::EnsureAtlasesBuilt();
std::string terrainPath = ModAtlas::GetMergedTerrainPath();
if (!terrainPath.empty())
{
std::wstring ourPath(atlasPath.begin(), atlasPath.end());
std::wstring ourPath(terrainPath.begin(), terrainPath.end());
LogUtil::Log("[WeaveLoader] getResourceAsStream: redirecting terrain.png to merged atlas");
return Original_GetResourceAsStream(&ourPath);
}
}
if (EndsWithPath(lower, L"items.png"))
{
ModAtlas::SetOverrideAtlasPath(1, std::string(path->begin(), path->end()));
ModAtlas::EnsureAtlasesBuilt();
std::string itemsPath = ModAtlas::GetMergedItemsPath();
if (!itemsPath.empty())
{
std::wstring ourPath(itemsPath.begin(), itemsPath.end());
LogUtil::Log("[WeaveLoader] getResourceAsStream: redirecting items.png to merged atlas");
return Original_GetResourceAsStream(&ourPath);
}
}
}
return Original_GetResourceAsStream ? Original_GetResourceAsStream(fileName) : nullptr;
return Original_GetResourceAsStream(fileName);
}
static bool s_loggedGetString = false;
@@ -2134,6 +2354,7 @@ namespace GameHooks
ModAtlas::SetVirtualAtlasDirectory(virtualAtlasDir);
HMODULE hMod = nullptr;
std::string modsPath;
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR)&Hooked_MinecraftInit, &hMod) && hMod)
{
char dllPath[MAX_PATH] = { 0 };
@@ -2144,14 +2365,15 @@ namespace GameHooks
if (dllPos != std::string::npos)
{
dllDir.resize(dllPos + 1);
std::string modsPath = dllDir + "mods";
ModAtlas::BuildAtlases(modsPath, gameResPath);
modsPath = dllDir + "mods";
goto atlas_done;
}
}
}
ModAtlas::BuildAtlases(base + "mods", gameResPath);
modsPath = base + "mods";
atlas_done:
ModAtlas::SetBasePaths(modsPath, gameResPath);
ModAtlas::EnsureAtlasesBuilt();
// Redirect terrain.png/items.png file opens to our merged atlases
// so the game loads mod textures without modifying vanilla files.

View File

@@ -17,6 +17,7 @@ typedef void (WINAPI *OutputDebugStringA_fn)(const char* lpOutputString);
typedef const wchar_t* (*GetString_fn)(int);
typedef void* (*GetResourceAsStream_fn)(const void* fileName);
typedef void (__fastcall *LoadUVs_fn)(void* thisPtr);
typedef void (__fastcall *PreStitchedTextureMapStitch_fn)(void* thisPtr);
typedef void* (__fastcall *RegisterIcon_fn)(void* thisPtr, const std::wstring& name);
typedef void* (__fastcall *ItemInstanceGetIcon_fn)(void* thisPtr);
typedef void (__fastcall *EntityRendererBindTextureResource_fn)(void* thisPtr, void* resourcePtr);
@@ -63,7 +64,13 @@ typedef void (__fastcall *AbstractContainerMenuBroadcastChanges_fn)(void* thisPt
typedef void (__fastcall *TexturesBindTextureResource_fn)(void* thisPtr, void* resourcePtr);
typedef int (__fastcall *TexturesLoadTextureByName_fn)(void* thisPtr, int texId, const std::wstring& resourceName);
typedef int (__fastcall *TexturesLoadTextureByIndex_fn)(void* thisPtr, int idx);
typedef void* (__fastcall *TexturesReadImage_fn)(void* thisPtr, int texId, const std::wstring& name);
typedef float (__fastcall *StitchedTextureUV_fn)(void* thisPtr, bool adjust);
typedef void (__fastcall *BufferedImageCtorFile_fn)(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive);
typedef void (__fastcall *BufferedImageCtorDLCPack_fn)(void* thisPtr, void* dlcPack, const std::wstring& file, bool filenameHasExtension);
typedef void* (__fastcall *TextureManagerCreateTexture_fn)(void* thisPtr, const std::wstring& name, int mode, int width, int height, int wrap, int format, int minFilter, int magFilter, bool mipmap, void* image);
typedef void (__fastcall *TextureTransferFromImage_fn)(void* thisPtr, void* image);
typedef void* (__fastcall *TexturePackGetImageResource_fn)(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive);
namespace GameHooks
{
@@ -78,6 +85,7 @@ namespace GameHooks
extern GetString_fn Original_GetString;
extern GetResourceAsStream_fn Original_GetResourceAsStream;
extern LoadUVs_fn Original_LoadUVs;
extern PreStitchedTextureMapStitch_fn Original_PreStitchedTextureMapStitch;
extern RegisterIcon_fn Original_RegisterIcon;
extern ItemInstanceGetIcon_fn Original_ItemInstanceGetIcon;
extern EntityRendererBindTextureResource_fn Original_EntityRendererBindTextureResource;
@@ -128,10 +136,17 @@ namespace GameHooks
extern TexturesBindTextureResource_fn Original_TexturesBindTextureResource;
extern TexturesLoadTextureByName_fn Original_TexturesLoadTextureByName;
extern TexturesLoadTextureByIndex_fn Original_TexturesLoadTextureByIndex;
extern TexturesReadImage_fn Original_TexturesReadImage;
extern StitchedTextureUV_fn Original_StitchedGetU0;
extern StitchedTextureUV_fn Original_StitchedGetU1;
extern StitchedTextureUV_fn Original_StitchedGetV0;
extern StitchedTextureUV_fn Original_StitchedGetV1;
extern BufferedImageCtorFile_fn Original_BufferedImageCtorFile;
extern BufferedImageCtorDLCPack_fn Original_BufferedImageCtorDLCPack;
extern TextureManagerCreateTexture_fn Original_TextureManagerCreateTexture;
extern TextureTransferFromImage_fn Original_TextureTransferFromImage;
extern TexturePackGetImageResource_fn Original_AbstractTexturePackGetImageResource;
extern TexturePackGetImageResource_fn Original_DLCTexturePackGetImageResource;
void Hooked_RunStaticCtors();
void __fastcall Hooked_MinecraftTick(void* thisPtr, bool bFirst, bool bUpdateTextures);
@@ -144,6 +159,7 @@ namespace GameHooks
const wchar_t* Hooked_GetString(int id);
void* Hooked_GetResourceAsStream(const void* fileName);
void __fastcall Hooked_LoadUVs(void* thisPtr);
void __fastcall Hooked_PreStitchedTextureMapStitch(void* thisPtr);
void* __fastcall Hooked_RegisterIcon(void* thisPtr, const std::wstring& name);
void* __fastcall Hooked_ItemInstanceGetIcon(void* thisPtr);
void __fastcall Hooked_EntityRendererBindTextureResource(void* thisPtr, void* resourcePtr);
@@ -194,10 +210,17 @@ namespace GameHooks
void __fastcall Hooked_TexturesBindTextureResource(void* thisPtr, void* resourcePtr);
int __fastcall Hooked_TexturesLoadTextureByName(void* thisPtr, int texId, const std::wstring& resourceName);
int __fastcall Hooked_TexturesLoadTextureByIndex(void* thisPtr, int idx);
void* __fastcall Hooked_TexturesReadImage(void* thisPtr, int texId, const std::wstring& name);
float __fastcall Hooked_StitchedGetU0(void* thisPtr, bool adjust);
float __fastcall Hooked_StitchedGetU1(void* thisPtr, bool adjust);
float __fastcall Hooked_StitchedGetV0(void* thisPtr, bool adjust);
float __fastcall Hooked_StitchedGetV1(void* thisPtr, bool adjust);
void __fastcall Hooked_BufferedImageCtorFile(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive);
void __fastcall Hooked_BufferedImageCtorDLCPack(void* thisPtr, void* dlcPack, const std::wstring& file, bool filenameHasExtension);
void* __fastcall Hooked_TextureManagerCreateTexture(void* thisPtr, const std::wstring& name, int mode, int width, int height, int wrap, int format, int minFilter, int magFilter, bool mipmap, void* image);
void __fastcall Hooked_TextureTransferFromImage(void* thisPtr, void* image);
void* __fastcall Hooked_AbstractTexturePackGetImageResource(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive);
void* __fastcall Hooked_DLCTexturePackGetImageResource(void* thisPtr, const std::wstring& file, bool filenameHasExtension, bool bTitleUpdateTexture, const std::wstring& drive);
void SetAtlasLocationPointers(void* blocksLocation, void* itemsLocation);
void SetTileTilesArray(void* tilesArray);
void SetSummonSymbols(void* levelAddEntity,

View File

@@ -722,6 +722,79 @@ bool HookManager::Install(const SymbolResolver& symbols)
}
}
if (symbols.pTexturesReadImage)
{
if (MH_CreateHook(symbols.pTexturesReadImage,
reinterpret_cast<void*>(&GameHooks::Hooked_TexturesReadImage),
reinterpret_cast<void**>(&GameHooks::Original_TexturesReadImage)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Textures::readImage");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Textures::readImage");
}
}
if (symbols.pTextureManagerCreateTexture)
{
if (MH_CreateHook(symbols.pTextureManagerCreateTexture,
reinterpret_cast<void*>(&GameHooks::Hooked_TextureManagerCreateTexture),
reinterpret_cast<void**>(&GameHooks::Original_TextureManagerCreateTexture)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook TextureManager::createTexture");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked TextureManager::createTexture");
}
}
if (symbols.pTextureTransferFromImage)
{
if (MH_CreateHook(symbols.pTextureTransferFromImage,
reinterpret_cast<void*>(&GameHooks::Hooked_TextureTransferFromImage),
reinterpret_cast<void**>(&GameHooks::Original_TextureTransferFromImage)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook Texture::transferFromImage");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked Texture::transferFromImage");
}
}
if (symbols.pAbstractTexturePackGetImageResource)
{
if (MH_CreateHook(symbols.pAbstractTexturePackGetImageResource,
reinterpret_cast<void*>(&GameHooks::Hooked_AbstractTexturePackGetImageResource),
reinterpret_cast<void**>(&GameHooks::Original_AbstractTexturePackGetImageResource)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook AbstractTexturePack::getImageResource");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked AbstractTexturePack::getImageResource");
}
}
if (symbols.pDLCTexturePackGetImageResource)
{
if (MH_CreateHook(symbols.pDLCTexturePackGetImageResource,
reinterpret_cast<void*>(&GameHooks::Hooked_DLCTexturePackGetImageResource),
reinterpret_cast<void**>(&GameHooks::Original_DLCTexturePackGetImageResource)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook DLCTexturePack::getImageResource");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked DLCTexturePack::getImageResource");
}
}
// BufferedImage constructor hooks disabled: the work is now handled in
// Textures::readImage for stability during boot.
if (symbols.pStitchedGetU0)
{
if (MH_CreateHook(symbols.pStitchedGetU0,
@@ -790,6 +863,20 @@ bool HookManager::Install(const SymbolResolver& symbols)
symbols.pEntityLerpMotion,
symbols.pEntitySetPos);
if (symbols.pPreStitchedTextureMapStitch)
{
if (MH_CreateHook(symbols.pPreStitchedTextureMapStitch,
reinterpret_cast<void*>(&GameHooks::Hooked_PreStitchedTextureMapStitch),
reinterpret_cast<void**>(&GameHooks::Original_PreStitchedTextureMapStitch)) != MH_OK)
{
LogUtil::Log("[WeaveLoader] Warning: Failed to hook PreStitchedTextureMap::stitch");
}
else
{
LogUtil::Log("[WeaveLoader] Hooked PreStitchedTextureMap::stitch (atlas type tracking)");
}
}
if (symbols.pLoadUVs && symbols.pSimpleIconCtor && symbols.pOperatorNew)
{
ModAtlas::SetInjectSymbols(symbols.pSimpleIconCtor, symbols.pOperatorNew);

View File

@@ -3,6 +3,7 @@
#include <Windows.h>
#include <MinHook.h>
#include <algorithm>
#include <cwctype>
#include <cstring>
#include <fstream>
#include <sstream>
@@ -18,6 +19,18 @@ namespace ModAtlas
{
static std::string s_mergedDir;
static std::string s_virtualAtlasDir;
static std::string s_modsPath;
static std::string s_gameResPath;
static std::string s_overrideTerrainPath;
static std::string s_overrideItemsPath;
static std::string s_lastTerrainBasePath;
static std::string s_lastItemsBasePath;
static int s_lastTerrainBaseWidth = 0;
static int s_lastTerrainBaseHeight = 0;
static int s_lastItemsBaseWidth = 0;
static int s_lastItemsBaseHeight = 0;
static void* s_lastTerrainImagePtr = nullptr;
static void* s_lastItemsImagePtr = nullptr;
static std::string s_mergedTerrainPage1Path;
static std::string s_mergedItemsPage1Path;
static std::vector<ModTextureEntry> s_blockEntries;
@@ -29,15 +42,32 @@ namespace ModAtlas
static bool s_hasItemsPage0Mods = false;
static int s_terrainPages = 0;
static int s_itemPages = 0;
static int s_terrainBaseRows = 32;
static int s_terrainRows = 32;
static float s_terrainVScale = 1.0f;
static int s_itemRows = 16;
static float s_itemVScale = 1.0f;
static void* s_simpleIconCtor = nullptr;
static void* (*s_operatorNew)(size_t) = nullptr;
static std::unordered_map<std::wstring, void*> s_modIcons;
static std::unordered_map<void*, int> s_iconAtlasType;
struct IconRouteInfo { int atlasType; int page; };
static std::unordered_map<void*, IconRouteInfo> s_iconRoutes;
static RegisterIcon_fn s_originalRegisterIcon = nullptr;
static thread_local bool s_hasPendingPage = false;
static thread_local int s_pendingAtlasType = -1;
static thread_local int s_pendingPage = 0;
static thread_local bool s_buildInProgress = false;
static bool s_applyMergedToBufferedImage = false;
// CreateFileW hook: redirect game file opens to merged atlases
static std::wstring s_mergedTerrainW;
static std::wstring s_mergedItemsW;
static std::wstring s_vanillaTerrainW;
static std::wstring s_vanillaItemsW;
typedef HANDLE (WINAPI *CreateFileW_fn)(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
static CreateFileW_fn s_originalCreateFileW = nullptr;
// iconType is at offset 8 in PreStitchedTextureMap (verified via getIconType disassembly)
@@ -109,6 +139,23 @@ namespace ModAtlas
return *data != nullptr;
}
static bool SavePngToBytes(const unsigned char* img, int w, int h,
std::vector<unsigned char>& outBytes)
{
outBytes.clear();
if (!img || w <= 0 || h <= 0) return false;
auto writeFunc = [](void* context, void* data, int size)
{
auto* bytes = reinterpret_cast<std::vector<unsigned char>*>(context);
const unsigned char* p = reinterpret_cast<const unsigned char*>(data);
bytes->insert(bytes->end(), p, p + size);
};
int ok = stbi_write_png_to_func(writeFunc, &outBytes, w, h, 4, img, w * 4);
return ok != 0 && !outBytes.empty();
}
static bool FileExists(const std::string& path)
{
DWORD attr = GetFileAttributesA(path.c_str());
@@ -128,15 +175,16 @@ namespace ModAtlas
return baseDir + "\\" + stem + "_p" + std::to_string(page) + ".png";
}
static void Blit16x16(unsigned char* dst, int dstW, int dstH, int dstX, int dstY,
const unsigned char* src, int srcW, int srcH)
static void BlitIcon(unsigned char* dst, int dstW, int dstH, int dstX, int dstY, int iconSize,
const unsigned char* src, int srcW, int srcH)
{
for (int y = 0; y < 16; y++)
if (iconSize <= 0) return;
for (int y = 0; y < iconSize; y++)
{
for (int x = 0; x < 16; x++)
for (int x = 0; x < iconSize; x++)
{
int sx = (srcW > 0) ? (x * srcW / 16) : 0;
int sy = (srcH > 0) ? (y * srcH / 16) : 0;
int sx = (srcW > 0) ? (x * srcW / iconSize) : 0;
int sy = (srcH > 0) ? (y * srcH / iconSize) : 0;
int di = ((dstY + y) * dstW + (dstX + x)) * 4;
int si = (sy * srcW + sx) * 4;
if (si < srcW * srcH * 4 && di < dstW * dstH * 4)
@@ -168,69 +216,120 @@ namespace ModAtlas
return true;
}
static size_t BuildAtlasPage(const std::string& vanillaPath, const std::string& outPath,
static int ComputeIconSize(int imgW, int imgH, int gridCols, int baseRows)
{
const int sizeW = (gridCols > 0) ? (imgW / gridCols) : 0;
const int sizeH = (baseRows > 0) ? (imgH / baseRows) : 0;
if (sizeW <= 0 && sizeH <= 0) return 0;
if (sizeW <= 0) return sizeH;
if (sizeH <= 0) return sizeW;
return (sizeW < sizeH) ? sizeW : sizeH;
}
static size_t BuildAtlasPageExpanded(const std::string& vanillaPath, const std::string& outPath,
const std::vector<std::pair<std::string, std::string>>& modTextures, size_t startIndex,
int atlasType, int page,
int gridCols, int gridRows, int iconSize,
int gridCols, int baseRows,
int& outIconSize, int& outTotalRows,
std::vector<ModTextureEntry>& entries)
{
int imgW = gridCols * iconSize;
int imgH = gridRows * iconSize;
unsigned char* img = (unsigned char*)calloc(imgW * imgH, 4);
if (!img) return false;
outIconSize = 0;
outTotalRows = baseRows;
int vw = 0, vh = 0, vc = 0;
bool loadedVanilla = false;
unsigned char* vanilla = nullptr;
if (!vanillaPath.empty())
{
unsigned char* vanilla = stbi_load(vanillaPath.c_str(), &vw, &vh, &vc, 4);
if (vanilla)
{
int copyW = (vw < imgW) ? vw : imgW;
int copyH = (vh < imgH) ? vh : imgH;
for (int y = 0; y < copyH; y++)
memcpy(img + y * imgW * 4, vanilla + y * vw * 4, copyW * 4);
stbi_image_free(vanilla);
loadedVanilla = true;
}
}
vanilla = stbi_load(vanillaPath.c_str(), &vw, &vh, &vc, 4);
if (!loadedVanilla)
if (!vanilla)
{
LogUtil::Log("[WeaveLoader] ModAtlas: ERROR could not load vanilla atlas base '%s' (atlasType=%d page=%d)",
LogUtil::Log("[WeaveLoader] ModAtlas: ERROR could not load atlas base '%s' (atlasType=%d page=%d)",
vanillaPath.c_str(), atlasType, page);
free(img);
return 0;
}
// Find all empty (fully transparent) cells in the atlas
int iconSize = ComputeIconSize(vw, vh, gridCols, baseRows);
if (iconSize <= 0)
{
LogUtil::Log("[WeaveLoader] ModAtlas: ERROR invalid atlas size %dx%d for baseRows=%d cols=%d",
vw, vh, baseRows, gridCols);
stbi_image_free(vanilla);
return 0;
}
const int baseW = gridCols * iconSize;
const int baseH = baseRows * iconSize;
if (vw != baseW || vh != baseH)
{
LogUtil::Log("[WeaveLoader] ModAtlas: atlas base size %dx%d does not match expected %dx%d (icon=%d). Using cropped base.",
vw, vh, baseW, baseH, iconSize);
}
std::vector<unsigned char> baseImg((size_t)baseW * (size_t)baseH * 4, 0);
const int copyW = (vw < baseW) ? vw : baseW;
const int copyH = (vh < baseH) ? vh : baseH;
for (int y = 0; y < copyH; y++)
{
memcpy(baseImg.data() + (size_t)y * baseW * 4,
vanilla + (size_t)y * vw * 4,
(size_t)copyW * 4);
}
stbi_image_free(vanilla);
// Find all empty (fully transparent) cells in the base atlas
std::vector<std::pair<int, int>> emptyCells;
for (int row = 0; row < gridRows; row++)
for (int row = 0; row < baseRows; row++)
{
for (int col = 0; col < gridCols; col++)
{
if (IsCellEmpty(img, imgW, imgH, col * iconSize, row * iconSize, iconSize))
if (IsCellEmpty(baseImg.data(), baseW, baseH, col * iconSize, row * iconSize, iconSize))
emptyCells.push_back({ row, col });
}
}
LogUtil::Log("[WeaveLoader] ModAtlas: found %zu empty cells in %dx%d atlas",
emptyCells.size(), gridCols, gridRows);
const size_t totalMods = (startIndex < modTextures.size()) ? (modTextures.size() - startIndex) : 0;
const size_t baseCapacity = emptyCells.size();
const size_t extraNeeded = (totalMods > baseCapacity) ? (totalMods - baseCapacity) : 0;
const int extraRows = (extraNeeded > 0)
? (int)((extraNeeded + (size_t)gridCols - 1) / (size_t)gridCols)
: 0;
const int totalRows = baseRows + extraRows;
const int imgW = baseW;
const int imgH = totalRows * iconSize;
unsigned char* img = (unsigned char*)calloc((size_t)imgW * (size_t)imgH, 4);
if (!img)
return 0;
// Copy base atlas into the top portion of the expanded atlas
for (int y = 0; y < baseH; y++)
memcpy(img + (size_t)y * imgW * 4, baseImg.data() + (size_t)y * baseW * 4, (size_t)baseW * 4);
LogUtil::Log("[WeaveLoader] ModAtlas: base %dx%d (rows=%d icon=%d) emptyCells=%zu extraRows=%d totalRows=%d",
baseW, baseH, baseRows, iconSize, emptyCells.size(), extraRows, totalRows);
size_t consumed = 0;
size_t cellIdx = 0;
size_t extraIdx = 0;
for (size_t texIdx = startIndex; texIdx < modTextures.size(); ++texIdx)
{
const auto& tex = modTextures[texIdx];
const std::string& iconName = tex.first;
const std::string& path = tex.second;
if (cellIdx >= emptyCells.size())
int row = 0;
int col = 0;
if (cellIdx < emptyCells.size())
{
LogUtil::Log("[WeaveLoader] ModAtlas: page %d full for atlasType=%d (stopped at %s)",
page, atlasType, iconName.c_str());
break;
row = emptyCells[cellIdx].first;
col = emptyCells[cellIdx].second;
cellIdx++;
}
else
{
row = baseRows + (int)(extraIdx / (size_t)gridCols);
col = (int)(extraIdx % (size_t)gridCols);
extraIdx++;
}
int sw = 0, sh = 0, sc = 0;
@@ -241,19 +340,18 @@ namespace ModAtlas
continue;
}
int row = emptyCells[cellIdx].first;
int col = emptyCells[cellIdx].second;
cellIdx++;
Blit16x16(img, imgW, imgH, col * iconSize, row * iconSize, src, sw, sh);
BlitIcon(img, imgW, imgH, col * iconSize, row * iconSize, iconSize, src, sw, sh);
stbi_image_free(src);
std::wstring wname(iconName.begin(), iconName.end());
entries.push_back({ wname, atlasType, page, row, col });
consumed++;
LogUtil::Log("[WeaveLoader] ModAtlas: placed '%s' at page=%d row=%d col=%d",
iconName.c_str(), page, row, col);
if (consumed <= 20)
{
LogUtil::Log("[WeaveLoader] ModAtlas: placed '%s' at page=%d row=%d col=%d",
iconName.c_str(), page, row, col);
}
}
std::string dir = outPath.substr(0, outPath.find_last_of("\\/"));
@@ -262,6 +360,9 @@ namespace ModAtlas
int ok = stbi_write_png(outPath.c_str(), imgW, imgH, 4, img, imgW * 4);
free(img);
if (!ok) return 0;
outIconSize = iconSize;
outTotalRows = totalRows;
return consumed;
}
@@ -296,7 +397,7 @@ namespace ModAtlas
int col = (int)(consumed % (size_t)gridCols);
consumed++;
Blit16x16(img, imgW, imgH, col * iconSize, row * iconSize, src, sw, sh);
BlitIcon(img, imgW, imgH, col * iconSize, row * iconSize, iconSize, src, sw, sh);
stbi_image_free(src);
std::wstring wname(iconName.begin(), iconName.end());
@@ -314,12 +415,16 @@ namespace ModAtlas
return consumed;
}
std::string BuildAtlases(const std::string& modsPath, const std::string& gameResPath)
static std::string BuildAtlasesInternal(const std::string& modsPath,
const std::string& terrainBasePath,
const std::string& itemsBasePath)
{
s_buildInProgress = true;
s_blockEntries.clear();
s_itemEntries.clear();
s_modIcons.clear();
s_iconRoutes.clear();
s_iconAtlasType.clear();
s_hasModTextures = false;
s_hasTerrainAtlas = false;
s_hasItemsAtlas = false;
@@ -329,6 +434,10 @@ namespace ModAtlas
s_itemPages = 0;
s_mergedTerrainPage1Path.clear();
s_mergedItemsPage1Path.clear();
s_terrainRows = s_terrainBaseRows;
s_terrainVScale = 1.0f;
s_itemRows = 16;
s_itemVScale = 1.0f;
std::vector<std::pair<std::string, std::string>> blockPaths, itemPaths;
FindModTextures(modsPath, blockPaths, itemPaths);
@@ -336,6 +445,7 @@ namespace ModAtlas
if (blockPaths.empty() && itemPaths.empty())
{
LogUtil::Log("[WeaveLoader] ModAtlas: no mod textures found");
s_buildInProgress = false;
return "";
}
@@ -355,54 +465,62 @@ namespace ModAtlas
s_mergedTerrainPage1Path = BuildPageOutputPath(s_mergedDir, "terrain", 1);
s_mergedItemsPage1Path = BuildPageOutputPath(s_mergedDir, "items", 1);
std::string vanillaTerrainPath = gameResPath + "\\terrain.png";
if (!blockPaths.empty())
{
size_t cursor = 0;
int page = 1; // page 0 remains vanilla; modded blocks use dedicated pages
while (cursor < blockPaths.size())
{
std::string outPath = BuildPageOutputPath(s_mergedDir, "terrain", page);
// World block rendering binds the terrain atlas once for a whole pass.
// Keep terrain page 1 as a vanilla+mod merged atlas so both vanilla and
// modded block icons resolve in the same draw pass.
size_t placed = BuildAtlasPage(vanillaTerrainPath, outPath, blockPaths, cursor, 0, page, 16, 32, 16, s_blockEntries);
if (placed == 0)
break;
if (!s_virtualAtlasDir.empty() && page > 0)
{
std::string vpath = BuildVirtualPageOutputPath(s_virtualAtlasDir, "terrain", page);
if (!vpath.empty())
CopyFileA(outPath.c_str(), vpath.c_str(), FALSE);
}
cursor += placed;
page++;
}
if (cursor > 0)
const std::string outPath = BuildPageOutputPath(s_mergedDir, "terrain", 0);
int iconSize = 0;
int totalRows = s_terrainBaseRows;
size_t placed = BuildAtlasPageExpanded(terrainBasePath, outPath, blockPaths, 0, 0, 0, 16, s_terrainBaseRows,
iconSize, totalRows, s_blockEntries);
if (placed > 0)
{
s_hasModTextures = true;
s_hasTerrainAtlas = true;
s_terrainPages = page;
LogUtil::Log("[WeaveLoader] ModAtlas: built terrain pages count=%d", s_terrainPages);
s_hasTerrainPage0Mods = true;
s_terrainPages = 1; // only page 0
s_terrainRows = (totalRows > 0) ? totalRows : s_terrainBaseRows;
if (s_terrainRows > 0)
s_terrainVScale = (float)s_terrainBaseRows / (float)s_terrainRows;
LogUtil::Log("[WeaveLoader] ModAtlas: built terrain atlas rows=%d (scaleV=%.4f)",
s_terrainRows, s_terrainVScale);
if (!s_virtualAtlasDir.empty())
{
std::string vpath = BuildVirtualPageOutputPath(s_virtualAtlasDir, "terrain", 0);
if (!vpath.empty())
CopyFileA(outPath.c_str(), vpath.c_str(), FALSE);
}
}
if (cursor < blockPaths.size())
if (placed < blockPaths.size())
{
LogUtil::Log("[WeaveLoader] ModAtlas: WARNING terrain overflow, dropped %zu textures",
blockPaths.size() - cursor);
blockPaths.size() - placed);
}
}
if (!itemPaths.empty())
{
int itemIconSize = 16;
if (!itemsBasePath.empty())
{
int iw = 0, ih = 0, ic = 0;
unsigned char* baseItems = stbi_load(itemsBasePath.c_str(), &iw, &ih, &ic, 4);
if (baseItems)
{
int computed = ComputeIconSize(iw, ih, 16, 16);
if (computed > 0)
itemIconSize = computed;
stbi_image_free(baseItems);
}
}
size_t cursor = 0;
int page = 1; // page 0 remains vanilla; modded items use dedicated pages
while (cursor < itemPaths.size())
{
std::string outPath = BuildPageOutputPath(s_mergedDir, "items", page);
size_t placed = BuildModOnlyAtlasPage(outPath, itemPaths, cursor, 1, page, 16, 16, 16, s_itemEntries);
size_t placed = BuildModOnlyAtlasPage(outPath, itemPaths, cursor, 1, page, 16, 16, itemIconSize, s_itemEntries);
if (placed == 0)
break;
if (!s_virtualAtlasDir.empty() && page > 0)
@@ -431,9 +549,350 @@ namespace ModAtlas
}
}
s_buildInProgress = false;
return s_hasModTextures ? s_mergedDir : "";
}
void SetBasePaths(const std::string& modsPath, const std::string& gameResPath)
{
s_modsPath = modsPath;
s_gameResPath = gameResPath;
}
void SetOverrideAtlasPath(int atlasType, const std::string& path)
{
if (atlasType == 0) s_overrideTerrainPath = path;
else if (atlasType == 1) s_overrideItemsPath = path;
}
static void UpdateLastBaseDims(int atlasType, int w, int h)
{
if (atlasType == 0)
{
s_lastTerrainBaseWidth = w;
s_lastTerrainBaseHeight = h;
}
else if (atlasType == 1)
{
s_lastItemsBaseWidth = w;
s_lastItemsBaseHeight = h;
}
}
static bool BaseDimsUnchanged(int atlasType, int w, int h)
{
if (atlasType == 0)
return (w == s_lastTerrainBaseWidth) && (h == s_lastTerrainBaseHeight);
if (atlasType == 1)
return (w == s_lastItemsBaseWidth) && (h == s_lastItemsBaseHeight);
return false;
}
static bool BuildTerrainFromBaseRgba(const unsigned char* baseRgba, int baseW, int baseH,
std::vector<unsigned char>& outRgba,
int& outW, int& outH)
{
outRgba.clear();
outW = 0;
outH = 0;
if (!baseRgba || baseW <= 0 || baseH <= 0)
return false;
if (s_blockEntries.empty())
return false;
int iconSize = ComputeIconSize(baseW, baseH, 16, s_terrainBaseRows);
if (iconSize <= 0)
return false;
outW = iconSize * 16;
outH = iconSize * ((s_terrainRows > 0) ? s_terrainRows : s_terrainBaseRows);
if (outW <= 0 || outH <= 0)
return false;
outRgba.assign(static_cast<size_t>(outW) * static_cast<size_t>(outH) * 4, 0);
const int copyW = (baseW < outW) ? baseW : outW;
const int copyH = (baseH < outH) ? baseH : outH;
for (int y = 0; y < copyH; y++)
{
const int srcRow = y * baseW * 4;
const int dstRow = y * outW * 4;
memcpy(outRgba.data() + dstRow, baseRgba + srcRow, static_cast<size_t>(copyW) * 4);
}
std::vector<std::pair<std::string, std::string>> blockPaths, itemPaths;
FindModTextures(s_modsPath, blockPaths, itemPaths);
std::unordered_map<std::string, std::string> pathMap;
pathMap.reserve(blockPaths.size());
for (const auto& p : blockPaths)
pathMap[p.first] = p.second;
for (const auto& e : s_blockEntries)
{
auto it = pathMap.find(std::string(e.iconName.begin(), e.iconName.end()));
if (it == pathMap.end())
continue;
int iw = 0, ih = 0, ic = 0;
unsigned char* src = stbi_load(it->second.c_str(), &iw, &ih, &ic, 4);
if (!src)
continue;
const int dstX = e.col * iconSize;
const int dstY = e.row * iconSize;
BlitIcon(outRgba.data(), outW, outH, dstX, dstY, iconSize, src, iw, ih);
stbi_image_free(src);
}
return true;
}
bool OverrideAtlasFromBufferedImage(int atlasType, void* bufferedImage)
{
if (!bufferedImage)
return false;
if (s_modsPath.empty() || s_gameResPath.empty())
return false;
// BufferedImage layout: int* data[10] at offset 0, width at +0x50, height at +0x54.
// We treat data[0] as ARGB 32-bit pixels (as used by Texture::transferFromImage).
char* base = static_cast<char*>(bufferedImage);
int** data = reinterpret_cast<int**>(base);
int* pixels = data[0];
int w = *reinterpret_cast<int*>(base + 0x50);
int h = *reinterpret_cast<int*>(base + 0x54);
if (!pixels || w <= 0 || h <= 0)
return false;
if (atlasType == 0)
{
if (bufferedImage == s_lastTerrainImagePtr)
return false;
s_lastTerrainImagePtr = bufferedImage;
}
else if (atlasType == 1)
{
if (bufferedImage == s_lastItemsImagePtr)
return false;
s_lastItemsImagePtr = bufferedImage;
}
std::vector<unsigned char> rgba(static_cast<size_t>(w) * static_cast<size_t>(h) * 4);
for (int i = 0; i < w * h; i++)
{
unsigned int argb = static_cast<unsigned int>(pixels[i]);
rgba[i * 4 + 0] = (argb >> 16) & 0xFF; // R
rgba[i * 4 + 1] = (argb >> 8) & 0xFF; // G
rgba[i * 4 + 2] = (argb >> 0) & 0xFF; // B
rgba[i * 4 + 3] = (argb >> 24) & 0xFF; // A
}
std::vector<unsigned char> mergedRgba;
int outW = w;
int outH = h;
bool ok = false;
if (atlasType == 0 && s_hasModTextures && !s_blockEntries.empty())
{
ok = BuildTerrainFromBaseRgba(rgba.data(), w, h, mergedRgba, outW, outH);
}
else if (atlasType == 0)
{
std::vector<unsigned char> pngBytes;
if (!SavePngToBytes(rgba.data(), w, h, pngBytes))
return false;
const std::string tempDir = s_gameResPath + "\\modloader\\temp";
CreateDirectoryA(tempDir.c_str(), nullptr);
const std::string tempPath = tempDir + "\\terrain.png";
{
std::ofstream out(tempPath, std::ios::binary);
if (!out.is_open())
return false;
out.write(reinterpret_cast<const char*>(pngBytes.data()), static_cast<std::streamsize>(pngBytes.size()));
}
s_applyMergedToBufferedImage = true;
SetOverrideAtlasPath(0, tempPath);
ok = EnsureAtlasesBuilt();
s_applyMergedToBufferedImage = false;
UpdateLastBaseDims(0, w, h);
if (!ok)
return false;
const std::string mergedPath = GetMergedTerrainPath();
if (mergedPath.empty())
return false;
int mw = 0, mh = 0, mc = 0;
unsigned char* merged = stbi_load(mergedPath.c_str(), &mw, &mh, &mc, 4);
if (!merged)
return false;
outW = (mw > 0) ? mw : w;
outH = (mh > 0) ? mh : h;
mergedRgba.assign(merged, merged + (mw * mh * 4));
stbi_image_free(merged);
}
else
{
// Do not override item atlases here (keep mod item paging stable).
return false;
}
if (!ok || mergedRgba.empty())
return false;
if (atlasType == 0 && !s_mergedDir.empty())
{
std::vector<unsigned char> pngBytes;
if (SavePngToBytes(mergedRgba.data(), outW, outH, pngBytes))
{
const std::string outPath = BuildPageOutputPath(s_mergedDir, "terrain", 0);
std::ofstream out(outPath, std::ios::binary);
if (out.is_open())
out.write(reinterpret_cast<const char*>(pngBytes.data()), static_cast<std::streamsize>(pngBytes.size()));
}
}
const bool needsResize = (outW != w) || (outH != h);
int* target = pixels;
if (needsResize)
{
target = new int[outW * outH];
std::fill(target, target + (outW * outH), 0);
}
const int copyW = outW;
const int copyH = outH;
for (int y = 0; y < copyH; y++)
{
for (int x = 0; x < copyW; x++)
{
const int si = (y * outW + x) * 4;
const int di = y * outW + x;
unsigned int argb = (static_cast<unsigned int>(mergedRgba[si + 3]) << 24) |
(static_cast<unsigned int>(mergedRgba[si + 0]) << 16) |
(static_cast<unsigned int>(mergedRgba[si + 1]) << 8) |
(static_cast<unsigned int>(mergedRgba[si + 2]) << 0);
target[di] = static_cast<int>(argb);
}
}
if (needsResize)
{
delete[] data[0];
for (int i = 1; i < 10; i++)
{
if (data[i])
{
delete[] data[i];
data[i] = nullptr;
}
}
data[0] = target;
*reinterpret_cast<int*>(base + 0x50) = outW;
*reinterpret_cast<int*>(base + 0x54) = outH;
}
return true;
}
bool EnsureAtlasesBuilt()
{
if (s_buildInProgress)
return s_hasModTextures;
if (s_modsPath.empty() || s_gameResPath.empty())
return false;
s_buildInProgress = true;
// Pre-scan for mods so we can avoid nuking a valid atlas when a pack
// path can't be loaded (e.g., DLC packs not backed by filesystem files).
std::vector<std::pair<std::string, std::string>> blockPaths, itemPaths;
FindModTextures(s_modsPath, blockPaths, itemPaths);
std::string terrainPath = !s_overrideTerrainPath.empty()
? s_overrideTerrainPath
: (s_gameResPath + "\\terrain.png");
std::string itemsPath = !s_overrideItemsPath.empty()
? s_overrideItemsPath
: (s_gameResPath + "\\items.png");
if (!s_applyMergedToBufferedImage &&
terrainPath == s_lastTerrainBasePath &&
itemsPath == s_lastItemsBasePath &&
!s_mergedDir.empty())
{
s_buildInProgress = false;
return s_hasModTextures;
}
// Fallback to default if override path doesn't exist
if (!s_overrideTerrainPath.empty() && !FileExists(terrainPath))
{
LogUtil::Log("[WeaveLoader] ModAtlas: override terrain path missing, falling back to default");
terrainPath = s_gameResPath + "\\terrain.png";
}
if (!s_overrideItemsPath.empty() && !FileExists(itemsPath))
{
LogUtil::Log("[WeaveLoader] ModAtlas: override items path missing, falling back to default");
itemsPath = s_gameResPath + "\\items.png";
}
if (!blockPaths.empty())
{
int tw = 0, th = 0, tc = 0;
unsigned char* probe = stbi_load(terrainPath.c_str(), &tw, &th, &tc, 4);
if (!probe)
{
LogUtil::Log("[WeaveLoader] ModAtlas: cannot load terrain base '%s' (keeping existing atlas)",
terrainPath.c_str());
s_buildInProgress = false;
return s_hasModTextures;
}
stbi_image_free(probe);
UpdateLastBaseDims(0, tw, th);
}
if (!itemPaths.empty())
{
int iw = 0, ih = 0, ic = 0;
unsigned char* probe = stbi_load(itemsPath.c_str(), &iw, &ih, &ic, 4);
if (probe)
{
stbi_image_free(probe);
UpdateLastBaseDims(1, iw, ih);
}
}
BuildAtlasesInternal(s_modsPath, terrainPath, itemsPath);
s_lastTerrainBasePath = terrainPath;
s_lastItemsBasePath = itemsPath;
// If the CreateFileW hook is installed, refresh redirect targets after rebuild.
if (s_originalCreateFileW)
{
std::string mergedTerrain = GetMergedTerrainPath();
std::string mergedItems = GetMergedItemsPath();
if (!mergedTerrain.empty())
s_mergedTerrainW = std::wstring(mergedTerrain.begin(), mergedTerrain.end());
if (!mergedItems.empty())
s_mergedItemsW = std::wstring(mergedItems.begin(), mergedItems.end());
}
return s_hasModTextures;
}
std::string BuildAtlases(const std::string& modsPath, const std::string& gameResPath)
{
SetBasePaths(modsPath, gameResPath);
s_overrideTerrainPath.clear();
s_overrideItemsPath.clear();
s_lastTerrainBasePath.clear();
s_lastItemsBasePath.clear();
s_lastTerrainBaseWidth = 0;
s_lastTerrainBaseHeight = 0;
s_lastItemsBaseWidth = 0;
s_lastItemsBaseHeight = 0;
return EnsureAtlasesBuilt() ? s_mergedDir : "";
}
void SetVirtualAtlasDirectory(const std::string& dir)
{
s_virtualAtlasDir = dir;
@@ -486,19 +945,42 @@ namespace ModAtlas
const std::vector<ModTextureEntry>& GetItemEntries() { return s_itemEntries; }
bool HasModTextures() { return s_hasModTextures; }
void NoteIconAtlasType(void* iconPtr, int atlasType)
{
if (!iconPtr) return;
s_iconAtlasType[iconPtr] = atlasType;
}
bool GetIconAtlasType(void* iconPtr, int& outAtlasType)
{
auto it = s_iconAtlasType.find(iconPtr);
if (it == s_iconAtlasType.end())
return false;
outAtlasType = it->second;
return true;
}
bool GetAtlasScale(int atlasType, float& outUScale, float& outVScale)
{
if (atlasType == 0)
{
outUScale = 1.0f;
outVScale = s_terrainVScale;
return true;
}
if (atlasType == 1)
{
outUScale = 1.0f;
outVScale = s_itemVScale;
return true;
}
return false;
}
// Per-atlas-type textureMap pointers, saved during CreateModIcons for FixupModIcons.
static void* s_terrainTextureMap = nullptr;
static void* s_itemsTextureMap = nullptr;
// CreateFileW hook: redirect game file opens to merged atlases
static std::wstring s_mergedTerrainW;
static std::wstring s_mergedItemsW;
static std::wstring s_vanillaTerrainW;
static std::wstring s_vanillaItemsW;
typedef HANDLE (WINAPI *CreateFileW_fn)(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
static CreateFileW_fn s_originalCreateFileW = nullptr;
static bool EndsWith(const wchar_t* path, const wchar_t* suffix)
{
size_t pathLen = wcslen(path);
@@ -507,12 +989,31 @@ namespace ModAtlas
return _wcsicmp(path + pathLen - suffLen, suffix) == 0;
}
static bool IsGeneratedAtlasPath(const wchar_t* path)
{
if (!path) return false;
std::wstring lower;
lower.reserve(wcslen(path));
for (const wchar_t* p = path; *p; ++p)
{
wchar_t c = (*p == L'\\') ? L'/' : *p;
lower.push_back((wchar_t)towlower(c));
}
return (lower.find(L"/mods/modloader/generated/") != std::wstring::npos) ||
(lower.find(L"/modloader/") != std::wstring::npos);
}
static HANDLE WINAPI Hooked_CreateFileW(
LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
if (lpFileName && s_hasModTextures)
if (s_buildInProgress)
{
return s_originalCreateFileW(lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}
if (lpFileName && s_hasModTextures && !IsGeneratedAtlasPath(lpFileName))
{
if (!s_mergedTerrainW.empty() && EndsWith(lpFileName, L"\\terrain.png"))
{
@@ -631,6 +1132,7 @@ namespace ModAtlas
ctor(icon, &e.iconName, &e.iconName, u0, v0, u1, v1);
s_modIcons[e.iconName] = icon;
s_iconRoutes[icon] = { iconType, e.page };
NoteIconAtlasType(icon, iconType);
LogUtil::Log("[WeaveLoader] ModAtlas: created icon '%ls' (atlas=%d, page=%d, row=%d, col=%d)",
e.iconName.c_str(), iconType, e.page, e.row, e.col);
}
@@ -638,9 +1140,9 @@ namespace ModAtlas
};
if (iconType == 0)
create(s_blockEntries, 1.0f / 32.0f);
create(s_blockEntries, 1.0f / (float)((s_terrainRows > 0) ? s_terrainRows : 32));
else if (iconType == 1)
create(s_itemEntries, 1.0f / 16.0f);
create(s_itemEntries, 1.0f / (float)((s_itemRows > 0) ? s_itemRows : 16));
LogUtil::Log("[WeaveLoader] ModAtlas: s_modIcons now has %zu entries total", s_modIcons.size());
}

View File

@@ -20,6 +20,21 @@ namespace ModAtlas
int row, col; // Grid position in atlas
};
/// Set base paths used by the atlas builder (mods dir + game res dir).
void SetBasePaths(const std::string& modsPath, const std::string& gameResPath);
/// Optional: override the base atlas path for a specific type (0=terrain, 1=items).
/// Useful for texture packs that live outside the default res directory.
void SetOverrideAtlasPath(int atlasType, const std::string& path);
/// Capture a loaded BufferedImage as the base atlas, rebuild merged atlases,
/// and replace the BufferedImage contents with the merged atlas.
bool OverrideAtlasFromBufferedImage(int atlasType, void* bufferedImage);
/// Build atlases if needed. Returns true if mod atlases are available.
bool EnsureAtlasesBuilt();
/// Legacy entry point (kept for compatibility).
/// Call before textures->stitch(). Returns path to generated dir, or empty if none.
std::string BuildAtlases(const std::string& modsPath, const std::string& gameResPath);
void SetVirtualAtlasDirectory(const std::string& dir);
@@ -68,6 +83,13 @@ namespace ModAtlas
void* LookupModIcon(const std::wstring& name);
bool TryGetIconRoute(void* iconPtr, int& outAtlasType, int& outPage);
/// Track atlas type for icons (vanilla + mod) so UV scaling can be applied.
void NoteIconAtlasType(void* iconPtr, int atlasType);
bool GetIconAtlasType(void* iconPtr, int& outAtlasType);
/// Query atlas UV scale for a given atlas type (used when atlas height expands).
bool GetAtlasScale(int atlasType, float& outUScale, float& outVScale);
/// Called by icon UV hooks so bind hooks can route to the right page.
void NotifyIconSampled(void* iconPtr);

View File

@@ -61,6 +61,7 @@ static const char* SYM_PRESENT = "?Present@C4JRender@@QEAAXXZ";
static const char* SYM_GET_STRING = "?GetString@CMinecraftApp@@SAPEB_WH@Z";
static const char* SYM_GET_RESOURCE_AS_STREAM = "?getResourceAsStream@InputStream@@SAPEAV1@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z";
static const char* SYM_LOAD_UVS = "?loadUVs@PreStitchedTextureMap@@AEAAXXZ";
static const char* SYM_PRESTITCHED_STITCH = "?stitch@PreStitchedTextureMap@@QEAAXXZ";
static const char* SYM_SIMPLE_ICON_CTOR = "??0SimpleIcon@@QEAA@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@0MMMM@Z";
static const char* SYM_OPERATOR_NEW = "??2@YAPEAX_K@Z";
static const char* SYM_REGISTER_ICON = "?registerIcon@PreStitchedTextureMap@@UEAAPEAVIcon@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z";
@@ -115,10 +116,17 @@ static const char* SYM_TEXTURES_BIND_RESOURCE = "?bindTexture@Textures@@QEAAXPEA
static const char* SYM_TEXTURES_LOAD_BY_NAME = "?loadTexture@Textures@@AEAAHW4_TEXTURE_NAME@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z";
static const char* SYM_TEXTURES_LOAD_BY_INDEX_PUBLIC = "?loadTexture@Textures@@QEAAHH@Z";
static const char* SYM_TEXTURES_LOAD_BY_INDEX_PRIVATE = "?loadTexture@Textures@@AEAAHH@Z";
static const char* SYM_TEXTURES_READIMAGE = "?readImage@Textures@@QEAAPEAVBufferedImage@@W4_TEXTURE_NAME@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z";
static const char* SYM_STITCHED_GETU0 = "?getU0@StitchedTexture@@UEBAM_N@Z";
static const char* SYM_STITCHED_GETU1 = "?getU1@StitchedTexture@@UEBAM_N@Z";
static const char* SYM_STITCHED_GETV0 = "?getV0@StitchedTexture@@UEBAM_N@Z";
static const char* SYM_STITCHED_GETV1 = "?getV1@StitchedTexture@@UEBAM_N@Z";
static const char* SYM_BUFFEREDIMAGE_CTOR_FILE = "??0BufferedImage@@QEAA@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@_N10@Z";
static const char* SYM_BUFFEREDIMAGE_CTOR_DLC = "??0BufferedImage@@QEAA@PEAVDLCPack@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@_N@Z";
static const char* SYM_TEXTUREMANAGER_CREATETEXTURE = "?createTexture@TextureManager@@QEAAPEAVTexture@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@HHHHHHH_NPEAVBufferedImage@@@Z";
static const char* SYM_TEXTURE_TRANSFERFROMIMAGE = "?transferFromImage@Texture@@QEAAXPEAVBufferedImage@@@Z";
static const char* SYM_ABSTRACT_TEXPACK_GETIMAGE = "?getImageResource@AbstractTexturePack@@UEAAPEAVBufferedImage@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@_N10@Z";
static const char* SYM_DLC_TEXPACK_GETIMAGE = "?getImageResource@DLCTexturePack@@UEAAPEAVBufferedImage@@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@_N10@Z";
static const char* SYM_MINECRAFT_SETLEVEL = "?setLevel@Minecraft@@QEAAXPEAVMultiPlayerLevel@@HV?$shared_ptr@VPlayer@@@std@@_N2@Z";
static const char* SYM_LEVEL_ADDENTITY = "?addEntity@Level@@UEAA_NV?$shared_ptr@VEntity@@@std@@@Z";
static const char* SYM_ENTITYIO_NEWBYID = "?newById@EntityIO@@SA?AV?$shared_ptr@VEntity@@@std@@HPEAVLevel@@@Z";
@@ -226,6 +234,9 @@ bool SymbolResolver::ResolveGameFunctions()
}
pGetResourceAsStream = Resolve(SYM_GET_RESOURCE_AS_STREAM);
pLoadUVs = Resolve(SYM_LOAD_UVS);
pPreStitchedTextureMapStitch = Resolve(SYM_PRESTITCHED_STITCH);
if (!pPreStitchedTextureMapStitch)
pPreStitchedTextureMapStitch = ResolveExactProcName(m_moduleBase, "PreStitchedTextureMap::stitch");
pSimpleIconCtor = Resolve(SYM_SIMPLE_ICON_CTOR);
pOperatorNew = Resolve(SYM_OPERATOR_NEW);
pRegisterIcon = Resolve(SYM_REGISTER_ICON);
@@ -287,10 +298,21 @@ bool SymbolResolver::ResolveGameFunctions()
pTexturesLoadTextureByIndex = Resolve(SYM_TEXTURES_LOAD_BY_INDEX_PUBLIC);
if (!pTexturesLoadTextureByIndex)
pTexturesLoadTextureByIndex = Resolve(SYM_TEXTURES_LOAD_BY_INDEX_PRIVATE);
pTexturesReadImage = Resolve(SYM_TEXTURES_READIMAGE);
pStitchedGetU0 = Resolve(SYM_STITCHED_GETU0);
pStitchedGetU1 = Resolve(SYM_STITCHED_GETU1);
pStitchedGetV0 = Resolve(SYM_STITCHED_GETV0);
pStitchedGetV1 = Resolve(SYM_STITCHED_GETV1);
pBufferedImageCtorFile = Resolve(SYM_BUFFEREDIMAGE_CTOR_FILE);
pBufferedImageCtorDLCPack = Resolve(SYM_BUFFEREDIMAGE_CTOR_DLC);
pTextureManagerCreateTexture = Resolve(SYM_TEXTUREMANAGER_CREATETEXTURE);
pTextureTransferFromImage = Resolve(SYM_TEXTURE_TRANSFERFROMIMAGE);
pAbstractTexturePackGetImageResource = Resolve(SYM_ABSTRACT_TEXPACK_GETIMAGE);
if (!pAbstractTexturePackGetImageResource)
pAbstractTexturePackGetImageResource = ResolveExactProcName(m_moduleBase, "AbstractTexturePack::getImageResource");
pDLCTexturePackGetImageResource = Resolve(SYM_DLC_TEXPACK_GETIMAGE);
if (!pDLCTexturePackGetImageResource)
pDLCTexturePackGetImageResource = ResolveExactProcName(m_moduleBase, "DLCTexturePack::getImageResource");
pMinecraftSetLevel = Resolve(SYM_MINECRAFT_SETLEVEL);
pLevelAddEntity = Resolve(SYM_LEVEL_ADDENTITY);
pEntityIONewById = Resolve(SYM_ENTITYIO_NEWBYID);
@@ -335,7 +357,13 @@ bool SymbolResolver::ResolveGameFunctions()
if (!pOperatorNew) pOperatorNew = GetProcAddress(GetModuleHandleA("vcruntime140d.dll"), SYM_OPERATOR_NEW);
if (!pOperatorNew) pOperatorNew = GetProcAddress(GetModuleHandle(nullptr), SYM_OPERATOR_NEW);
if (!pSimpleIconCtor) PdbParser::DumpMatching("??0SimpleIcon@@");
if (!pBufferedImageCtorFile && !pBufferedImageCtorDLCPack) PdbParser::DumpMatching("??0BufferedImage@@");
if (!pTextureManagerCreateTexture) PdbParser::DumpMatching("createTexture@TextureManager");
if (!pTextureTransferFromImage) PdbParser::DumpMatching("transferFromImage@Texture");
if (!pAbstractTexturePackGetImageResource) PdbParser::DumpMatching("getImageResource@AbstractTexturePack");
if (!pDLCTexturePackGetImageResource) PdbParser::DumpMatching("getImageResource@DLCTexturePack");
if (!pLoadUVs) PdbParser::DumpMatching("loadUVs@PreStitchedTextureMap");
if (!pPreStitchedTextureMapStitch) PdbParser::DumpMatching("stitch@PreStitchedTextureMap");
auto logSym = [](const char* name, void* ptr) {
if (ptr)
@@ -354,6 +382,7 @@ bool SymbolResolver::ResolveGameFunctions()
logSym("CMinecraftApp::GetString", pGetString);
logSym("InputStream::getResourceAsStream", pGetResourceAsStream);
logSym("PreStitchedTextureMap::loadUVs", pLoadUVs);
logSym("PreStitchedTextureMap::stitch", pPreStitchedTextureMapStitch);
logSym("SimpleIcon::SimpleIcon", pSimpleIconCtor);
logSym("operator new", pOperatorNew);
logSym("registerIcon", pRegisterIcon);
@@ -407,10 +436,17 @@ bool SymbolResolver::ResolveGameFunctions()
logSym("Textures::bindTexture(ResourceLocation)", pTexturesBindTextureResource);
logSym("Textures::loadTexture(TEXTURE_NAME,wstring)", pTexturesLoadTextureByName);
logSym("Textures::loadTexture(int)", pTexturesLoadTextureByIndex);
logSym("Textures::readImage(TEXTURE_NAME,wstring)", pTexturesReadImage);
logSym("StitchedTexture::getU0", pStitchedGetU0);
logSym("StitchedTexture::getU1", pStitchedGetU1);
logSym("StitchedTexture::getV0", pStitchedGetV0);
logSym("StitchedTexture::getV1", pStitchedGetV1);
logSym("BufferedImage::BufferedImage(file)", pBufferedImageCtorFile);
logSym("BufferedImage::BufferedImage(DLCPack)", pBufferedImageCtorDLCPack);
logSym("TextureManager::createTexture", pTextureManagerCreateTexture);
logSym("Texture::transferFromImage", pTextureTransferFromImage);
logSym("AbstractTexturePack::getImageResource", pAbstractTexturePackGetImageResource);
logSym("DLCTexturePack::getImageResource", pDLCTexturePackGetImageResource);
logSym("Minecraft::setLevel", pMinecraftSetLevel);
logSym("Level::addEntity", pLevelAddEntity);
logSym("EntityIO::newById", pEntityIONewById);

View File

@@ -20,6 +20,7 @@ public:
void* pGetString = nullptr; // CMinecraftApp::GetString(int)
void* pGetResourceAsStream = nullptr; // InputStream::getResourceAsStream(wstring)
void* pLoadUVs = nullptr; // PreStitchedTextureMap::loadUVs()
void* pPreStitchedTextureMapStitch = nullptr; // PreStitchedTextureMap::stitch()
void* pSimpleIconCtor = nullptr; // SimpleIcon::SimpleIcon(wstring,wstring,float*4)
void* pOperatorNew = nullptr; // global operator new(size_t) - for texture injection
void* pRegisterIcon = nullptr; // PreStitchedTextureMap::registerIcon(const wstring&)
@@ -73,10 +74,17 @@ public:
void* pTexturesBindTextureResource = nullptr; // Textures::bindTexture(ResourceLocation*)
void* pTexturesLoadTextureByName = nullptr; // Textures::loadTexture(TEXTURE_NAME,const wstring&)
void* pTexturesLoadTextureByIndex = nullptr; // Textures::loadTexture(int)
void* pTexturesReadImage = nullptr; // Textures::readImage(TEXTURE_NAME,const wstring&)
void* pStitchedGetU0 = nullptr; // StitchedTexture::getU0(bool) const
void* pStitchedGetU1 = nullptr; // StitchedTexture::getU1(bool) const
void* pStitchedGetV0 = nullptr; // StitchedTexture::getV0(bool) const
void* pStitchedGetV1 = nullptr; // StitchedTexture::getV1(bool) const
void* pBufferedImageCtorFile = nullptr; // BufferedImage::BufferedImage(const wstring&,bool,bool,const wstring&)
void* pBufferedImageCtorDLCPack = nullptr; // BufferedImage::BufferedImage(DLCPack*,const wstring&,bool)
void* pTextureManagerCreateTexture = nullptr; // TextureManager::createTexture(wstring,int,int,int,int,int,int,int,bool,BufferedImage*)
void* pTextureTransferFromImage = nullptr; // Texture::transferFromImage(BufferedImage*)
void* pAbstractTexturePackGetImageResource = nullptr; // AbstractTexturePack::getImageResource
void* pDLCTexturePackGetImageResource = nullptr; // DLCTexturePack::getImageResource
void* pMinecraftSetLevel = nullptr; // Minecraft::setLevel(MultiPlayerLevel*,int,shared_ptr<Player>,bool,bool)
void* pLevelAddEntity = nullptr; // Level::addEntity(shared_ptr<Entity>)
void* pEntityIONewById = nullptr; // EntityIO::newById(int,Level*)