Skip to content

Commit

Permalink
Add bugfix for missing thorn textures
Browse files Browse the repository at this point in the history
  • Loading branch information
Malacath-92 committed Jan 27, 2022
1 parent b8decf2 commit 56ef479
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 14 deletions.
Binary file added res/extra_thorns.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions res/playlunky64.rc
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
ENTITIES_JSON JSON_FILE "entities.json"
TEXTURES_JSON JSON_FILE "textures.json"
PET_HEADS PNG_FILE "pet_heads.png"
EXTRA_THORNS PNG_FILE "extra_thorns.png"
1 change: 1 addition & 0 deletions res/resource_playlunky64.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
#define TEXTURES_JSON 103
#define PNG_FILE 104
#define PET_HEADS 105
#define EXTRA_THORNS 106
137 changes: 137 additions & 0 deletions source/playlunky/mod/bug_fixes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include "bug_fixes.h"

#include "../../res/resource_playlunky64.h"
#include "dds_conversion.h"
#include "log.h"
#include "playlunky_settings.h"
#include "util/image.h"
#include "util/on_scope_exit.h"
#include "virtual_filesystem.h"

#include "detour/sigscan.h"

#include <Windows.h>
#include <detours.h>
#include <spel2.h>

std::int64_t g_ExtraThornsTextureId{};
using SetupThorns = void(Entity*);
SetupThorns* g_SetupThornsTrampoline{ nullptr };
using GetNeighbouringThorns = uint32_t(Entity* thorns, float offset_x, float offset_y);
GetNeighbouringThorns* g_GetNeighbouringThorns{ nullptr };

bool InitBugFixes(VirtualFilesystem& /*vfs*/,
const PlaylunkySettings& settings,
const std::filesystem::path& db_folder,
const std::filesystem::path& original_data_folder)
{
namespace fs = std::filesystem;

// Extract extra thorns
if (settings.GetBool("bug_fixes", "missing_thorns", true))
{
const auto extra_thorns_path = original_data_folder / "Data/Textures/extra_thorns.png";
if (!fs::exists(extra_thorns_path))
{
auto acquire_png_resource = [](LPSTR resource)
{
HMODULE this_module = GetModuleHandle("playlunky64.dll");
if (HRSRC png_resource = FindResource(this_module, resource, MAKEINTRESOURCE(PNG_FILE)))
{
if (HGLOBAL png_data = LoadResource(this_module, png_resource))
{
DWORD png_size = SizeofResource(this_module, png_resource);
return std::pair{ png_data, std::span<std::uint8_t>{ (std::uint8_t*)LockResource(png_data), png_size } };
}
}
return std::pair{ HGLOBAL{ NULL }, std::span<std::uint8_t>{} };
};
auto [extra_thorns_res, extra_thorns_png] = acquire_png_resource(MAKEINTRESOURCE(EXTRA_THORNS));
OnScopeExit release_resources{ [extra_thorns_res]
{
UnlockResource(extra_thorns_res);
} };

Image extra_thorns;
extra_thorns.Load(extra_thorns_png);
extra_thorns.Write(extra_thorns_path);
extra_thorns.Write("Mods/Extracted/Data/Textures/extra_thorns.png");

const auto extra_thorns_dds_path = db_folder / "Data/Textures/extra_thorns.DDS";
if (!ConvertRBGAToDds(extra_thorns.GetData(), extra_thorns.GetWidth(), extra_thorns.GetHeight(), extra_thorns_dds_path))
{
return false;
}
}

Spelunky_TextureDefinition extra_thorns_texture_def{
"Data/Textures/extra_thorns.DDS",
128 * 3,
128 * 2,
128,
128,
};
g_ExtraThornsTextureId = Spelunky_DefineTexture(extra_thorns_texture_def);

static constexpr auto decode_call = [](void* addr) -> void* {
return (char*)addr + (*(int32_t*)((char*)addr + 1)) + 5;
};

constexpr std::string_view g_SetupThornsFuncPattern{ "\x34\x01\xc0\xe0\x03\x08\xc8" };
g_SetupThornsTrampoline = static_cast<SetupThorns*>(SigScan::FindFunctionStart(SigScan::FindPattern("Spel2.exe", g_SetupThornsFuncPattern, true)));
g_GetNeighbouringThorns = static_cast<GetNeighbouringThorns*>(decode_call(SigScan::FindPattern("\xe8", g_SetupThornsTrampoline, (char*)g_SetupThornsTrampoline + 0x1000)));

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());

static constexpr auto c_SetupMissingThorns = [](Entity* thorns)
{
g_SetupThornsTrampoline(thorns);
const auto current_texture_tile = SpelunkyEntity_GetTextureTile(thorns);
if (current_texture_tile == 127)
{
const auto left = g_GetNeighbouringThorns(thorns, 0.0f, 1.0f) & 0x1;
const auto right = g_GetNeighbouringThorns(thorns, 0.0f, -1.0f) & 0x1;
const auto top = g_GetNeighbouringThorns(thorns, 1.0f, 0.0f) & 0x1;
const auto bottom = g_GetNeighbouringThorns(thorns, -1.0f, 0.0f) & 0x1;
//const auto mask = top | (bottom << 1) | (left << 2) | (right << 3);
const auto mask = left | (right << 1) | (top << 2) | (bottom << 3);
const auto texture_tile = [mask]() -> uint16_t
{
switch (mask)
{
case 0b0000:
return 0;
case 0b1111:
return 3;
case 0b0111:
return 1;
case 0b1011:
return 2;
case 0b1101:
return 4;
case 0b1110:
return 5;
default:
LogError("Thorns have missing textures but valid setup... why?");
return 127;
}
}();
if (texture_tile != 127)
{
SpelunkyEntity_SetTexture(thorns, g_ExtraThornsTextureId);
SpelunkyEntity_SetTextureTile(thorns, texture_tile);
}
}
};
DetourAttach((void**)&g_SetupThornsTrampoline, (SetupThorns*)c_SetupMissingThorns);

const LONG error = DetourTransactionCommit();
if (error != NO_ERROR)
{
LogError("Could not fix thorns textures: {}", error);
}
}

return true;
}
12 changes: 12 additions & 0 deletions source/playlunky/mod/bug_fixes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <filesystem>
#include <vector>

class PlaylunkySettings;
class VirtualFilesystem;

bool InitBugFixes(VirtualFilesystem& vfs,
const PlaylunkySettings& settings,
const std::filesystem::path& db_folder,
const std::filesystem::path& original_data_folder);
25 changes: 13 additions & 12 deletions source/playlunky/mod/mod_manager.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "mod_manager.h"

#include "bug_fixes.h"
#include "cache_audio_file.h"
#include "dds_conversion.h"
#include "decode_audio_file.h"
Expand Down Expand Up @@ -44,7 +45,7 @@ static constexpr ctll::fixed_string s_StringModFileRule{ "strings([0-9]{2})_mod\

ModManager::ModManager(std::string_view mods_root, const PlaylunkySettings& settings, VirtualFilesystem& vfs)
: mSpriteSheetMerger{ new SpriteSheetMerger{ settings } }
, mVfs{ &vfs }
, mVfs{ vfs }
, mModsRoot{ mods_root }
, mDeveloperMode{ settings.GetBool("settings", "enable_developer_mode", false) || settings.GetBool("script_settings", "enable_developer_mode", false) }
, mConsoleMode{ settings.GetBool("script_settings", "enable_developer_console", false) }
Expand Down Expand Up @@ -689,12 +690,9 @@ ModManager::ModManager(std::string_view mods_root, const PlaylunkySettings& sett
Spelunky_RegisterOnLoadFileFunc(FunctionPointer<Spelunky_LoadFileFunc, struct ModManagerLoadFile>(
[this](const char* file_path, SpelunkyAllocFun alloc_fun) -> SpelunkyFileInfo*
{
if (mVfs)
if (auto* file_info = mVfs.LoadFile(file_path, alloc_fun))
{
if (auto* file_info = mVfs->LoadFile(file_path, alloc_fun))
{
return file_info;
}
return file_info;
}
return nullptr;
}));
Expand Down Expand Up @@ -814,15 +812,18 @@ ModManager::~ModManager()

void ModManager::PostGameInit(const class PlaylunkySettings& settings)
{
const auto db_folder = mModsRoot / ".db";
const auto db_original_folder = db_folder / "Original";

if (mSpritePainter)
{
LogInfo("Setting up sprite painting...");
const auto db_folder = mModsRoot / ".db";
const auto db_original_folder = db_folder / "Original";
mSpritePainter->FinalizeSetup(db_original_folder, db_folder);
}

PatchCharacterDefinitions(*mVfs, settings);
PatchCharacterDefinitions(mVfs, settings);

InitBugFixes(mVfs, settings, db_folder, db_original_folder);

Spelunky_InitSoundManager([](const char* file_path)
{
Expand Down Expand Up @@ -884,7 +885,7 @@ bool ModManager::OnInput(std::uint32_t msg, std::uint64_t w_param, std::int64_t
}
void ModManager::Update()
{
if (mSpritePainter || (mSpriteHotLoader && mVfs))
if (mSpritePainter || mSpriteHotLoader)
{
const auto db_folder = mModsRoot / ".db";
const auto db_original_folder = db_folder / "Original";
Expand All @@ -893,9 +894,9 @@ void ModManager::Update()
mSpritePainter->Update(db_original_folder, db_folder);
}

if (mSpriteHotLoader && mVfs)
if (mSpriteHotLoader)
{
mSpriteHotLoader->Update(db_original_folder, db_folder, *mVfs);
mSpriteHotLoader->Update(db_original_folder, db_folder, mVfs);
}
}

Expand Down
2 changes: 1 addition & 1 deletion source/playlunky/mod/mod_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class ModManager
std::unique_ptr<SpritePainter> mSpritePainter;
std::unique_ptr<SpriteSheetMerger> mSpriteSheetMerger;
ScriptManager mScriptManager;
VirtualFilesystem* mVfs;
VirtualFilesystem& mVfs;

std::filesystem::path mModsRoot;

Expand Down
3 changes: 3 additions & 0 deletions source/playlunky/playlunky_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ PlaylunkySettings::PlaylunkySettings(std::string settings_file)
KnownSetting{ .Name{ "enable_customizable_sheets" }, .DefaultValue{ "on" }, .Comment{ "Enables the customizable sprite sheets feature, does not work in speedrun mode" } },
KnownSetting{ .Name{ "enable_luminance_scaling" }, .DefaultValue{ "on" }, .Comment{ "Scales luminance of customized images based on the color" } },
} },
KnownCategory{ { "bug_fixes" }, {
KnownSetting{ .Name{ "missing_thorns" }, .DefaultValue{ "on" }, .Comment{ "Adds textures for the missing jungle thorns configurations" } },
} },
KnownCategory{ { "key_bindings" }, {
KnownSetting{ .Name{ "console" }, .DefaultValue{ "0xc0" }, .Comment{ "Default 0xc0 == ~ for US" } },
KnownSetting{ .Name{ "console_alt" }, .DefaultValue{ "0xdc" }, .Comment{ "Default 0xdc == \\ for US" } },
Expand Down
2 changes: 1 addition & 1 deletion submodules/overlunky

0 comments on commit 56ef479

Please sign in to comment.