Custom textures

This commit is contained in:
Federico Cecchetto 2021-08-23 17:11:45 +02:00
parent e552146bda
commit c84d4c0dcc
10 changed files with 352 additions and 8 deletions

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "deps/discord-rpc"]
path = deps/discord-rpc
url = https://github.com/discord/discord-rpc.git
[submodule "deps/stb"]
path = deps/stb
url = https://github.com/nothings/stb.git

19
deps/premake/stb.lua vendored Normal file
View File

@ -0,0 +1,19 @@
stb = {
source = path.join(dependencies.basePath, "stb"),
}
function stb.import()
stb.includes()
end
function stb.includes()
includedirs {
stb.source
}
end
function stb.project()
end
table.insert(dependencies, stb)

View File

@ -239,9 +239,14 @@ namespace game_console
con.globals.left_x = con.globals.x;
con.globals.auto_complete_choice[0] = 0;
/*
game::R_AddCmdDrawTextWithCursor(con.buffer, 0x7FFFFFFF, console_font, 18, con.globals.x,
con.globals.y + con.globals.font_height, 1.0f, 1.0f, 0, color_white, 0,
con.cursor, '|');
*/
game::R_AddCmdDrawText(con.buffer, 0x7FFF, console_font, con.globals.x,
con.globals.y + con.globals.font_height, 1.0f, 1.0f, 0.0f, color_white, 0);
// check if using a prefixed '/' or not
const auto input = con.buffer[1] && (con.buffer[0] == '/' || con.buffer[0] == '\\')
@ -428,21 +433,16 @@ namespace game_console
bool console_char_event(const int localClientNum, const int key)
{
if (key == '`' || key == '~')
if (key == '`' || key == '~' || key == '|' || key == '\\')
{
return false;
}
if (key > 255)
if (key > 127)
{
return true;
}
if (key == '|' || key == '\\')
{
return false;
}
if (*game::keyCatchers & 1)
{
if (key == game::keyNum_t::K_TAB) // tab (auto complete)

137
src/component/images.cpp Normal file
View File

@ -0,0 +1,137 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "images.hpp"
#include "game_console.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/image.hpp>
#include <utils/io.hpp>
#include <utils/concurrency.hpp>
namespace images
{
namespace
{
utils::hook::detour load_texture_hook;
utils::hook::detour setup_texture_hook;
utils::concurrency::container<std::unordered_map<std::string, std::string>> overriden_textures;
static_assert(sizeof(game::GfxImage) == 104);
static_assert(offsetof(game::GfxImage, name) == (sizeof(game::GfxImage) - sizeof(void*)));
static_assert(offsetof(game::GfxImage, pixelData) == 56);
static_assert(offsetof(game::GfxImage, width) == 44);
static_assert(offsetof(game::GfxImage, height) == 46);
static_assert(offsetof(game::GfxImage, depth) == 48);
static_assert(offsetof(game::GfxImage, numElements) == 50);
std::optional<std::string> load_image(game::GfxImage* image)
{
std::string data{};
overriden_textures.access([&](const std::unordered_map<std::string, std::string>& textures)
{
if (const auto i = textures.find(image->name); i != textures.end())
{
data = i->second;
}
});
if (data.empty() && !utils::io::read_file(utils::string::va("images/%s.png", image->name), &data))
{
return {};
}
return { std::move(data) };
}
std::optional<utils::image> load_raw_image_from_file(game::GfxImage* image)
{
const auto image_file = load_image(image);
if (!image_file)
{
return {};
}
return utils::image(*image_file);
}
bool load_custom_texture(game::GfxImage* image)
{
auto raw_image = load_raw_image_from_file(image);
if (!raw_image)
{
return false;
}
image->imageFormat = 0x1000003;
image->resourceSize = -1;
D3D11_SUBRESOURCE_DATA data{};
data.SysMemPitch = raw_image->get_width() * 4;
data.SysMemSlicePitch = data.SysMemPitch * raw_image->get_height();
data.pSysMem = raw_image->get_buffer();
game::Image_Setup(image, raw_image->get_width(), raw_image->get_height(), image->depth, image->numElements,
image->imageFormat, DXGI_FORMAT_R8G8B8A8_UNORM, 0, image->name, &data);
return true;
}
std::unordered_map<std::string, bool> image_list;
void load_texture_stub(game::GfxImage* image, void* a2, int* a3)
{
if (image_list.find(image->name) == image_list.end())
{
//printf("Loading: %s\n", image->name);
image_list[image->name] = true;
}
try
{
if (load_custom_texture(image))
{
printf("Loaded custom image for: %s\n", image->name);
return;
}
}
catch (std::exception& e)
{
game_console::print(game_console::con_type_error, "Failed to load image %s: %s\n", image->name, e.what());
}
load_texture_hook.invoke<void>(image, a2, a3);
}
int setup_texture_stub(game::GfxImage* image, void* a2, void* a3)
{
if (image->resourceSize == -1)
{
return 0;
}
return setup_texture_hook.invoke<bool>(image, a2, a3);
}
}
void override_texture(std::string name, std::string data)
{
overriden_textures.access([&](std::unordered_map<std::string, std::string>& textures)
{
textures[std::move(name)] = std::move(data);
});
}
class component final : public component_interface
{
public:
void post_unpack() override
{
setup_texture_hook.create(game::base_address + 0x74A390, setup_texture_stub);
load_texture_hook.create(game::base_address + 0x2A7940, load_texture_stub);
}
};
}
REGISTER_COMPONENT(images::component)

6
src/component/images.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
namespace images
{
void override_texture(std::string name, std::string data);
}

View File

@ -1,4 +1,5 @@
#pragma once
#include <d3d11.h>
namespace game
{
@ -20,6 +21,36 @@ namespace game
char __pad0[13508];
};
enum $219904913BC1E6DB920C78C8CC0BD8F1
{
FL_GODMODE = 0x1,
FL_DEMI_GODMODE = 0x2,
FL_NOTARGET = 0x4,
FL_NO_KNOCKBACK = 0x8,
FL_NO_RADIUS_DAMAGE = 0x10,
FL_SUPPORTS_LINKTO = 0x20,
FL_NO_AUTO_ANIM_UPDATE = 0x40,
FL_GRENADE_TOUCH_DAMAGE = 0x80,
FL_STABLE_MISSILES = 0x100,
FL_REPEAT_ANIM_UPDATE = 0x200,
FL_VEHICLE_TARGET = 0x400,
FL_GROUND_ENT = 0x800,
FL_CURSOR_HINT = 0x1000,
FL_MISSILE_ATTRACTOR_OR_REPULSOR = 0x2000,
FL_WEAPON_BEING_GRABBED = 0x4000,
FL_DELETE = 0x8000,
FL_BOUNCE = 0x10000,
FL_MOVER_SLIDE = 0x20000,
FL_MOVING = 0x40000,
FL_DONT_AUTOBOLT_MISSILE_EFFECTS = 0x80000,
FL_DISABLE_MISSILE_STICK = 0x100000,
FL_NO_MELEE_TARGET = 0x2000000,
FL_DYNAMICPATH = 0x8000000,
FL_AUTO_BLOCKPATHS = 0x10000000,
FL_OBSTACLE = 0x20000000,
FL_BADPLACE_VOLUME = 0x80000000,
};
struct EntityState
{
char entityNum;
@ -33,7 +64,7 @@ namespace game
gclient_s* client;
char __pad2[0x4C];
char flags;
//char __pad3[392];
char __pad3[392];
}; // size = 760
struct Material
@ -757,4 +788,66 @@ namespace game
int emissiveTechType;
int forceTechType;
};
struct GfxImageLoadDef
{
char levelCount;
char numElements;
char pad[2];
int flags;
int format;
int resourceSize;
char data[1];
};
union $3FA29451CE6F1FA138A5ABAB84BE9676
{
ID3D11Texture1D* linemap;
ID3D11Texture2D* map;
ID3D11Texture3D* volmap;
ID3D11Texture2D* cubemap;
GfxImageLoadDef* loadDef;
};
struct GfxTexture
{
$3FA29451CE6F1FA138A5ABAB84BE9676 ___u0;
ID3D11ShaderResourceView* shaderView;
ID3D11ShaderResourceView* shaderViewAlternate;
};
struct Picmip
{
char platform[2];
};
struct CardMemory
{
int platform[2];
};
struct GfxImage
{
GfxTexture textures;
int flags;
int imageFormat;
int resourceSize;
char mapType;
char semantic;
char category;
char flags2;
Picmip picmip;
char track;
//CardMemory cardMemory;
unsigned short width;
unsigned short height;
unsigned short depth;
unsigned short numElements;
char pad3[4];
void* pixelData;
//GfxImageLoadDef *loadDef;
uint64_t streams[4];
const char* name;
};
}

View File

@ -22,6 +22,9 @@ namespace game
DB_EnumXAssets_Internal{0x4129F0};
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x3E4090};
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x414FF0};
WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{0x412F60};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x413D80};
WEAK symbol<int(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x413C40};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x618F90};
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x5A75D0};
@ -51,6 +54,9 @@ namespace game
WEAK symbol<char*(char* string)> I_CleanStr{0x620660};
WEAK symbol<char* (GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount,
uint32_t imageFlags, DXGI_FORMAT imageFormat, int a8, const char* name, const void* initData)> Image_Setup{0x74B2A0};
WEAK symbol<void(int clientNum, const char* menu, int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x5F0EE0};
WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x759BA0};
@ -70,6 +76,8 @@ namespace game
WEAK symbol<Font_s*(const char* font, int size)> R_RegisterFont{0x746FE0};
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x7472A0};
WEAK symbol<void()> R_SyncRenderThread{0x76E7D0};
WEAK symbol<void(const void* obj, void* pose, unsigned int entnum, unsigned int renderFxFlags, float* lightingOrigin,
float materialTime, __int64 a7, __int64 a8)> R_AddDObjToScene{0x775C40};
WEAK symbol<ScreenPlacement* ()> ScrPlace_GetViewPlacement{0x3E16A0};

View File

@ -22,6 +22,7 @@
#include <unordered_set>
#include <csetjmp>
#include <deque>
#include <optional>
#ifdef max
#undef max

53
src/utils/image.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <stdinc.hpp>
#include "image.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
namespace utils
{
image::image(const std::string& image_data)
{
int channels{};
auto* rgb_image = stbi_load_from_memory(reinterpret_cast<const uint8_t*>(image_data.data()), static_cast<int>(image_data.size()), &this->width, &this->height, &channels, 4);
if(!rgb_image)
{
throw std::runtime_error("Unable to load image");
}
auto _ = gsl::finally([rgb_image]()
{
stbi_image_free(rgb_image);
});
const auto size = this->width * this->height * 4;
this->data.resize(size);
std::memmove(this->data.data(), rgb_image, size);
}
int image::get_width() const
{
return this->width;
}
int image::get_height() const
{
return this->height;
}
const void* image::get_buffer() const
{
return this->data.data();
}
size_t image::get_size() const
{
return this->data.size();
}
const std::string& image::get_data() const
{
return this->data;
}
}

24
src/utils/image.hpp Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <string>
namespace utils
{
class image
{
public:
image(const std::string& data);
int get_width() const;
int get_height() const;
const void* get_buffer() const;
size_t get_size() const;
const std::string& get_data() const;
private:
int width{};
int height{};
std::string data{};
};
}