Initial public release of rem-essentials
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
#ifndef _FLPLUSPLUS_H_
|
||||
#define _FLPLUSPLUS_H_
|
||||
|
||||
#define FLPEXPORT __declspec(dllexport)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//typedef void (*flplusplus_cblatehook)(void*);
|
||||
//FLPEXPORT void flplusplus_add_latehook(flplusplus_cblatehook hkfunc, void *userData);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#pragma ms_struct on
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include "offsets.h"
|
||||
|
||||
class INI_Reader
|
||||
{
|
||||
public:
|
||||
INI_Reader();
|
||||
~INI_Reader();
|
||||
|
||||
bool open(LPCSTR path, bool throwExceptionOnFail);
|
||||
bool read_header();
|
||||
bool is_header(LPCSTR header);
|
||||
bool read_value();
|
||||
bool is_value(LPCSTR value);
|
||||
bool get_value_bool(UINT index);
|
||||
int get_value_int(UINT index);
|
||||
float get_value_float(UINT index);
|
||||
LPCSTR get_value_string(UINT index);
|
||||
void close();
|
||||
LPCSTR get_name_ptr();
|
||||
|
||||
private:
|
||||
BYTE data[0x1568];
|
||||
};
|
||||
|
||||
namespace Archetype
|
||||
{
|
||||
struct Ship
|
||||
{
|
||||
BYTE x00[0x14];
|
||||
UINT idsName;
|
||||
};
|
||||
|
||||
Ship* GetShip(UINT id);
|
||||
}
|
||||
|
||||
struct CShip
|
||||
{
|
||||
BYTE x00[0x88];
|
||||
Archetype::Ship* shiparch;
|
||||
};
|
||||
|
||||
struct IObjInspectImpl
|
||||
{
|
||||
BYTE data[0x10];
|
||||
CShip* ship;
|
||||
};
|
||||
|
||||
namespace Universe
|
||||
{
|
||||
struct IBase
|
||||
{
|
||||
BYTE data[0xC];
|
||||
UINT idsName;
|
||||
};
|
||||
|
||||
struct ISystem
|
||||
{
|
||||
BYTE data[0x68];
|
||||
UINT idsName;
|
||||
};
|
||||
|
||||
IBase* get_base(UINT id);
|
||||
ISystem* get_system(UINT id);
|
||||
}
|
||||
|
||||
bool IsMPServer();
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
#include "Freelancer.h"
|
||||
#include "Common.h"
|
||||
#include <cstdlib>
|
||||
|
||||
CShip* GetShip()
|
||||
{
|
||||
typedef IObjInspectImpl* GetPlayerIObjInspectImpl();
|
||||
IObjInspectImpl* playerIObjInspect = ((GetPlayerIObjInspectImpl*) OF_GET_PLAYER_INSPECT_IMPL)();
|
||||
|
||||
return !playerIObjInspect ? nullptr : playerIObjInspect->ship;
|
||||
}
|
||||
|
||||
std::wstring GetSystemName()
|
||||
{
|
||||
UINT currentSystemId = *((PUINT) OF_CURRENT_SYSTEM_ID);
|
||||
|
||||
if (!currentSystemId)
|
||||
return {};
|
||||
|
||||
UINT systemIds = Universe::get_system(currentSystemId)->idsName;
|
||||
|
||||
if (!systemIds)
|
||||
return {};
|
||||
|
||||
WCHAR buffer[64] = { 0 };
|
||||
GetFlString(systemIds, buffer, _countof(buffer));
|
||||
|
||||
return std::wstring(buffer);
|
||||
}
|
||||
|
||||
std::wstring GetBaseName()
|
||||
{
|
||||
UINT currentBaseId = *((PUINT) OF_CURRENT_BASE_ID);
|
||||
|
||||
if (!currentBaseId)
|
||||
return {};
|
||||
|
||||
UINT baseIds = Universe::get_base(currentBaseId)->idsName;
|
||||
|
||||
if (!baseIds)
|
||||
return {};
|
||||
|
||||
WCHAR buffer[64] = { 0 };
|
||||
GetFlString(baseIds, buffer, _countof(buffer));
|
||||
|
||||
return std::wstring(buffer);
|
||||
}
|
||||
|
||||
std::wstring GetShipName()
|
||||
{
|
||||
UINT currentShipId = GetShipId();
|
||||
|
||||
if (!currentShipId)
|
||||
return {};
|
||||
|
||||
Archetype::Ship* shiparch = Archetype::GetShip(currentShipId);
|
||||
|
||||
if (!shiparch)
|
||||
return {};
|
||||
|
||||
WCHAR buffer[64] = { 0 };
|
||||
GetFlString(shiparch->idsName, buffer, _countof(buffer));
|
||||
|
||||
return std::wstring(buffer);
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include "offsets.h"
|
||||
|
||||
std::wstring GetSystemName();
|
||||
std::wstring GetBaseName();
|
||||
std::wstring GetShipName();
|
||||
|
||||
inline UINT GetFlString(UINT ids, PWCHAR buffer, UINT bufferSize)
|
||||
{
|
||||
PDWORD resourceHandle = *((PDWORD*) OF_RESOURCES_HANDLE);
|
||||
|
||||
typedef UINT GetFlStringFunc(PDWORD, UINT, PWCHAR, UINT);
|
||||
return ((GetFlStringFunc*) OF_GET_FL_STRING)(resourceHandle, ids, buffer, bufferSize);
|
||||
}
|
||||
|
||||
// It is recommended to call this function rather than getting the CURRENT_SHIP_ID directly.
|
||||
// This is because Console hooks this function to make it so that the player has no ship sometimes.
|
||||
inline UINT GetShipId()
|
||||
{
|
||||
typedef UINT GetShipIdFunc();
|
||||
return ((GetShipIdFunc*) OF_GET_SHIP_ID)();
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
#include "codec.h"
|
||||
#include "offsets.h"
|
||||
#include "patch.h"
|
||||
#include "config.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
void codec::init()
|
||||
{
|
||||
// adoxa created a plugin called MP3 Codec Fix which fixes the "missing MP3 codec" spew warning the right way.
|
||||
// However, supposedly this fix doesn't work on Wine (probably due to the missing Fraunhofer codec), despite the audio working just fine.
|
||||
// Hence we apply patches to wipe out the MP3 codec spew warnings on Wine only.
|
||||
if (!config::is_wine())
|
||||
return;
|
||||
|
||||
//Patch out MP3 warnings
|
||||
auto soundManager = (DWORD) GetModuleHandleA("soundmanager.dll");
|
||||
if (soundManager)
|
||||
patch::patch_uint8(soundManager + F_OF_SOUNDMAN_MP3, 0xC3);
|
||||
|
||||
auto soundStreamer = (DWORD) GetModuleHandleA("soundstreamer.dll");
|
||||
if (soundStreamer)
|
||||
patch::patch_uint8(soundStreamer + F_OF_SOUNDSTR_MP3, 0xC3);
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
namespace codec {
|
||||
void init();
|
||||
}
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace config {
|
||||
void init_defaults();
|
||||
void init_from_file(const char *filename);
|
||||
void read_font_files(const char *filename);
|
||||
void EnsureInitialized();
|
||||
class ConfigData
|
||||
{
|
||||
public:
|
||||
float lodscale;
|
||||
float pbubblescale;
|
||||
float characterdetailscale;
|
||||
float asteroiddistscale;
|
||||
std::string savefoldername;
|
||||
bool saveindirectory;
|
||||
std::string screenshotsfoldername;
|
||||
bool screenshotsindirectory;
|
||||
bool altfullscreenscreenshots;
|
||||
bool altwindowedscreenshots;
|
||||
bool removestartlocationwarning;
|
||||
bool logtoconsole;
|
||||
float shippreviewscrollingspeed;
|
||||
bool shippreviewscrollinginverse;
|
||||
float shippreviewscrollingmindistance;
|
||||
float shippreviewscrollingmaxdistance;
|
||||
bool alwaysregeneraterestartfile;
|
||||
int failedtoinitsavesdirids;
|
||||
bool touchpadsupport;
|
||||
bool confinecursor;
|
||||
std::vector<std::string> fontfiles{};
|
||||
};
|
||||
ConfigData& get_config();
|
||||
bool is_wine();
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// maximum mumber of lines the output console should have
|
||||
static const WORD MAX_CONSOLE_LINES = 500;
|
||||
|
||||
|
||||
void RedirectIOToConsole()
|
||||
{
|
||||
int hConHandle;
|
||||
long lStdHandle;
|
||||
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
||||
FILE *fp;
|
||||
|
||||
// allocate a console for this app
|
||||
AllocConsole();
|
||||
|
||||
// set the screen buffer to be big enough to let us scroll text
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&coninfo);
|
||||
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
|
||||
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coninfo.dwSize);
|
||||
|
||||
// redirect unbuffered STDOUT to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
|
||||
freopen_s(&fp, "CONOUT$", "w", stdout);
|
||||
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDIN to the console
|
||||
|
||||
lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
|
||||
fp = _fdopen( hConHandle, "r" );
|
||||
freopen_s(&fp, "CONIN$", "w", stdin);
|
||||
setvbuf( stdin, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDERR to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
|
||||
freopen_s(&fp, "CONOUT$", "w", stderr);
|
||||
|
||||
setvbuf( stderr, NULL, _IONBF, 0 );
|
||||
|
||||
// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
|
||||
// point to console as well
|
||||
ios::sync_with_stdio();
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
void RedirectIOToConsole();
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#include "cursor.h"
|
||||
#include "config.h"
|
||||
#include "offsets.h"
|
||||
#include "patch.h"
|
||||
|
||||
int __fastcall UpdateMouseX(int mouseX, int increase)
|
||||
{
|
||||
MOUSE_X = mouseX + increase;
|
||||
if (MOUSE_X < 0)
|
||||
MOUSE_X = 0;
|
||||
else if (MOUSE_X > WINDOW_WIDTH - 1)
|
||||
MOUSE_X = WINDOW_WIDTH - 1;
|
||||
|
||||
return increase;
|
||||
}
|
||||
|
||||
int __fastcall UpdateMouseY(int mouseY, int increase)
|
||||
{
|
||||
MOUSE_Y = mouseY + increase;
|
||||
if (MOUSE_Y < 0)
|
||||
MOUSE_Y = 0;
|
||||
else if (MOUSE_Y > WINDOW_HEIGHT - 1)
|
||||
MOUSE_Y = WINDOW_HEIGHT - 1;
|
||||
|
||||
return increase;
|
||||
}
|
||||
|
||||
void cursor::hook_mouse_func(unsigned int address, void* func)
|
||||
{
|
||||
unsigned char dummy[5];
|
||||
patch::patch_uint16(address, 0xC289); // mov edx, eax
|
||||
patch::detour((unsigned char*) (address + 2), func, dummy, false);
|
||||
patch::patch_uint8(address + 7, 0x90);
|
||||
}
|
||||
|
||||
void cursor::init()
|
||||
{
|
||||
if (!config::get_config().confinecursor)
|
||||
return;
|
||||
|
||||
hook_mouse_func(OF_MOUSE_X_UPDATE, (void*) UpdateMouseX);
|
||||
hook_mouse_func(OF_MOUSE_Y_UPDATE, (void*) UpdateMouseY);
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace cursor {
|
||||
void hook_mouse_func(unsigned int address, void* func);
|
||||
void init();
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#include "fontresource.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <shlwapi.h>
|
||||
|
||||
void fontresource::init(LPCSTR fontDirectory)
|
||||
{
|
||||
// Add font resources
|
||||
for (const auto &fontFile : config::get_config().fontfiles) {
|
||||
char path[MAX_PATH];
|
||||
|
||||
// Create full path to font file
|
||||
strcpy_s(path, sizeof(path), fontDirectory);
|
||||
PathAppendA(path, fontFile.c_str());
|
||||
|
||||
if (!PathFileExists(path)) {
|
||||
logger::writeformat("path to font %s does not exist (%s)", fontFile.c_str(), path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AddFontResourceEx(path, FR_PRIVATE, nullptr))
|
||||
logger::writeformat("successfully added font %s", fontFile.c_str());
|
||||
else
|
||||
logger::writeformat("error adding font %s", fontFile.c_str());
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
namespace fontresource {
|
||||
void init(LPCSTR fontDirectory);
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
#include "graphics.h"
|
||||
#include "patch.h"
|
||||
#include "offsets.h"
|
||||
#include "config.h"
|
||||
#include "Common.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
config::ConfigData& cfg = config::get_config();
|
||||
|
||||
#define MIN_DETAIL_SCALE 1.0f
|
||||
#define MAX_DETAIL_SCALE 1000000.0f
|
||||
#define MAX_ASTEROID_DIST_SCALE 10.0f
|
||||
|
||||
float __fastcall multiply_lodranges_float(INI_Reader* reader, PVOID _edx, UINT index)
|
||||
{
|
||||
return reader->get_value_float(index) * cfg.lodscale;
|
||||
}
|
||||
|
||||
float __fastcall multiply_pbubble_float(INI_Reader* reader, PVOID _edx, UINT index)
|
||||
{
|
||||
return reader->get_value_float(index) * cfg.pbubblescale;
|
||||
}
|
||||
|
||||
float __fastcall multiply_characterdetail_float(INI_Reader* reader, PVOID _edx, UINT index)
|
||||
{
|
||||
return reader->get_value_float(index) * cfg.characterdetailscale;
|
||||
}
|
||||
|
||||
float __fastcall multiply_asteroiddist_float(INI_Reader* reader, PVOID _edx, UINT index)
|
||||
{
|
||||
return reader->get_value_float(index) * cfg.asteroiddistscale;
|
||||
}
|
||||
|
||||
bool patch_lodranges()
|
||||
{
|
||||
if(cfg.lodscale <= MIN_DETAIL_SCALE)
|
||||
return false;
|
||||
else if (cfg.lodscale > MAX_DETAIL_SCALE)
|
||||
cfg.lodscale = MAX_DETAIL_SCALE;
|
||||
|
||||
//distances
|
||||
patch::set_execute_read_write(OF_REN_DIST0, sizeof(float));
|
||||
*((float*)OF_REN_DIST0) *= cfg.lodscale;
|
||||
|
||||
static UINT multiplyLodsPtr = (UINT) &multiply_lodranges_float;
|
||||
patch::patch_uint32(OF_LODS_GET_VALUE, (UINT) &multiplyLodsPtr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool patch_pbubble()
|
||||
{
|
||||
if (cfg.pbubblescale <= MIN_DETAIL_SCALE)
|
||||
return false;
|
||||
else if (cfg.pbubblescale > MAX_DETAIL_SCALE)
|
||||
cfg.pbubblescale = MAX_DETAIL_SCALE;
|
||||
|
||||
static UINT multiplyPbubblePtr = (UINT) &multiply_pbubble_float;
|
||||
|
||||
patch::patch_uint32(OF_PBUBBLE_GET_VALUE0, (UINT) &multiplyPbubblePtr);
|
||||
patch::patch_uint32(OF_PBUBBLE_GET_VALUE1, (UINT) &multiplyPbubblePtr);
|
||||
|
||||
patch::set_execute_read_write(OF_REN_DIST1, sizeof(float));
|
||||
float ren_dist1 = *((float*)OF_REN_DIST1);
|
||||
|
||||
if (cfg.pbubblescale > 2.0f)
|
||||
ren_dist1 += (ren_dist1 / 10.0f) * (cfg.pbubblescale - 2.0f);
|
||||
|
||||
// 40000.0f is considered to be the maximum "safe" value
|
||||
if (ren_dist1 > 40000.0f)
|
||||
ren_dist1 = 40000.0f;
|
||||
|
||||
*((float*)OF_REN_DIST1) = ren_dist1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool patch_characterdetail()
|
||||
{
|
||||
if (cfg.characterdetailscale <= MIN_DETAIL_SCALE)
|
||||
return false;
|
||||
else if (cfg.characterdetailscale > MAX_DETAIL_SCALE)
|
||||
cfg.characterdetailscale = MAX_DETAIL_SCALE;
|
||||
|
||||
auto common = (DWORD) GetModuleHandleA("common.dll");
|
||||
if (!common)
|
||||
common = (DWORD) LoadLibraryA("common.dll");
|
||||
if (!common)
|
||||
return false;
|
||||
|
||||
UINT multiplyCharacterDetailPtr = (UINT) &multiply_characterdetail_float;
|
||||
UINT detailSwitchAddr = common + F_OF_BODYPART_DETAILSWITCH_GET_VALUE;
|
||||
|
||||
patch::patch_uint32(detailSwitchAddr, multiplyCharacterDetailPtr - detailSwitchAddr - 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool patch_asteroiddist()
|
||||
{
|
||||
if (cfg.asteroiddistscale <= MIN_DETAIL_SCALE)
|
||||
return false;
|
||||
else if (cfg.asteroiddistscale > MAX_ASTEROID_DIST_SCALE)
|
||||
cfg.asteroiddistscale = MAX_ASTEROID_DIST_SCALE;
|
||||
|
||||
static UINT multiplyAsteroidDistPtr = (UINT) &multiply_asteroiddist_float;
|
||||
patch::patch_uint32(OF_ASTEROID_DIST_GET_VALUE, (UINT) &multiplyAsteroidDistPtr);
|
||||
patch::patch_uint32(OF_AST_BILLBOARD_DIST_GET_VALUE, (UINT)&multiplyAsteroidDistPtr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool graphics::init_base_fixes()
|
||||
{
|
||||
auto common = (DWORD) GetModuleHandleA("common.dll");
|
||||
if (!common)
|
||||
common = (DWORD) LoadLibraryA("common.dll");
|
||||
if (!common)
|
||||
return false;
|
||||
|
||||
patch::patch_uint8(OF_VIDEODIALOG, 0x33); //disable unsupported video dialog
|
||||
patch::patch_uint16(OF_MAXTEXSIZE, 0x2000); //texture size bug fix
|
||||
//replace "Vibrocentric" string
|
||||
//FL tries to load this font over Agency FB, screws up UI if it finds it
|
||||
//if you have a font named '\b' you have big problems
|
||||
const char *garbageFont = "\b\0";
|
||||
unsigned int address = common + F_OF_VIBROCENTRICFONT_V11;
|
||||
patch::patch_bytes(address, (void*)garbageFont, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool graphics::init_detail_scaling()
|
||||
{
|
||||
patch_lodranges();
|
||||
patch_pbubble();
|
||||
if (!patch_characterdetail())
|
||||
return false;
|
||||
patch_asteroiddist();
|
||||
return true;
|
||||
}
|
||||
|
||||
void graphics::init()
|
||||
{
|
||||
init_base_fixes();
|
||||
init_detail_scaling();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
namespace graphics {
|
||||
void init();
|
||||
bool init_base_fixes();
|
||||
bool init_detail_scaling();
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
#ifndef _JUMPTABLE_H_
|
||||
#define _JUMPTABLE_H_
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
// Macro magic to produce a compact set of functions to
|
||||
// manually import functions
|
||||
#ifdef _MSC_VER
|
||||
#define JUMPTABLE( def, table, index ) __declspec(naked) def { __asm jmp table[index*8+4] }
|
||||
#else
|
||||
#define JUMPTABLE( def, table, index ) __attribute__((naked)) def { \
|
||||
__asm(".intel_syntax noprefix\n"); \
|
||||
__asm__("jmp _" #table "[" #index "*8+4]\n"); \
|
||||
__asm__(".att_syntax"); \
|
||||
}
|
||||
#endif
|
||||
#define FUNC_INITIALIZER(f) \
|
||||
static void f(void); \
|
||||
struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
|
||||
static void f(void)
|
||||
#define JUMPTABLE_INIT( dll, table ) \
|
||||
FUNC_INITIALIZER( table ## _Load) \
|
||||
{ \
|
||||
HMODULE library = LoadLibraryA(dll); \
|
||||
for(int i = 0; i < (sizeof(table) / sizeof(const char*)); i+=2) \
|
||||
{ \
|
||||
table[i + 1] = (const char*)GetProcAddress(library, table[i]); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
Vendored
+137
@@ -0,0 +1,137 @@
|
||||
#include "log.h"
|
||||
#include "config.h"
|
||||
#include "patch.h"
|
||||
#include "offsets.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <ctime>
|
||||
#include <stdarg.h>
|
||||
|
||||
static bool linked = false;
|
||||
static bool fdump_patched = false;
|
||||
static bool timestamps_enabled = false;
|
||||
static bool console_enabled = false;
|
||||
|
||||
typedef int (*pFDUMP)(DWORD, const char *, ...);
|
||||
static pFDUMP *FDUMP = nullptr;
|
||||
static pFDUMP fdump_original = nullptr;
|
||||
|
||||
static bool do_linking()
|
||||
{
|
||||
if (linked)
|
||||
return FDUMP != nullptr;
|
||||
|
||||
linked = true;
|
||||
|
||||
HMODULE dacom = GetModuleHandleA("dacom.dll");
|
||||
if (!dacom)
|
||||
dacom = LoadLibraryA("dacom.dll");
|
||||
|
||||
if (!dacom)
|
||||
return false;
|
||||
|
||||
FDUMP = (pFDUMP*)GetProcAddress(dacom, "FDUMP");
|
||||
return FDUMP != nullptr;
|
||||
}
|
||||
|
||||
void logger::writeline(const char *line)
|
||||
{
|
||||
#define ERRORCODE_NOTICE 0x100003
|
||||
if (do_linking() && *FDUMP)
|
||||
(*FDUMP)(ERRORCODE_NOTICE, "%s", line);
|
||||
}
|
||||
|
||||
static DWORD fdump_rem(DWORD errorCode, const char *fmt, ...)
|
||||
{
|
||||
char buffer[4096];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
std::time_t rawtime;
|
||||
std::tm* timeinfo;
|
||||
char timestamp[100];
|
||||
std::time(&rawtime);
|
||||
timeinfo = std::localtime(&rawtime);
|
||||
std::strftime(timestamp, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
|
||||
if(console_enabled) {
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
auto severity = (BYTE) (errorCode);
|
||||
|
||||
if (severity <= 1)
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
|
||||
else if (severity <= 2)
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
else
|
||||
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
|
||||
if (timestamps_enabled)
|
||||
printf("[%s] %s\n", timestamp, buffer);
|
||||
else
|
||||
printf("%s\n", buffer);
|
||||
}
|
||||
|
||||
if (timestamps_enabled)
|
||||
return fdump_original(errorCode, "[%s] %s", timestamp, buffer);
|
||||
|
||||
return fdump_original(errorCode, "%s", buffer);
|
||||
}
|
||||
|
||||
bool logger::enable_fdump(bool timestamps, bool console)
|
||||
{
|
||||
timestamps_enabled = timestamps_enabled || timestamps;
|
||||
console_enabled = console_enabled || console;
|
||||
|
||||
if (fdump_patched)
|
||||
return true;
|
||||
|
||||
if (!do_linking() || !FDUMP || !*FDUMP)
|
||||
return false;
|
||||
|
||||
fdump_original = *FDUMP;
|
||||
*FDUMP = (pFDUMP)fdump_rem;
|
||||
fdump_patched = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// FLServer has hard coded calls to a function called ServerLogF,
|
||||
// which prints non-timestamped messages,
|
||||
// so patch every call to show timestamped messages instead.
|
||||
bool logger::patch_serverlogf()
|
||||
{
|
||||
if (!enable_fdump(true, false))
|
||||
return false;
|
||||
|
||||
#define FLSERVER_BASE (0x400000)
|
||||
|
||||
// File offsets of ServerLogF calls
|
||||
DWORD serverLogCalls[] = {
|
||||
0xB152, 0xB18F, 0xB1CC, 0xB235, 0xB26D, 0xBCE4,
|
||||
0xBFD6, 0xCD03, 0x1398D, 0x13B1F, 0x13BA0
|
||||
};
|
||||
|
||||
// Hook all instances where ServerLogF is called
|
||||
unsigned char originalData[5];
|
||||
for (const DWORD serverLogCall : serverLogCalls) {
|
||||
auto *originalFunc = (unsigned char *)(serverLogCall + FLSERVER_BASE);
|
||||
patch::detour(originalFunc, (void*) fdump_rem, originalData, false);
|
||||
}
|
||||
|
||||
// Sets the server log function in remoteclient.dll
|
||||
// Never seen it being used but overwrite the function just in case
|
||||
patch::patch_uint32(OF_SERVER_LOG_FUNCTION_REF, (UINT) &fdump_rem);
|
||||
return true;
|
||||
}
|
||||
|
||||
void logger::writeformat(const char *fmt, ...)
|
||||
{
|
||||
char buffer[4096];
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end (args);
|
||||
logger::writeline(buffer);
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace logger {
|
||||
void writeline(const char *line);
|
||||
void writeformat(const char *fmt, ...);
|
||||
bool enable_fdump(bool timestamps, bool console);
|
||||
bool patch_serverlogf();
|
||||
}
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#define OF_REN_DIST0 (0x613EC8)
|
||||
#define OF_REN_DIST1 (0x5C8910)
|
||||
|
||||
#define OF_MAXTEXSIZE (0x41AD6F)
|
||||
|
||||
#define OF_VIDEODIALOG (0x5B16FC)
|
||||
|
||||
#define OF_PRINTSCREEN (0x425170)
|
||||
|
||||
#define F_OF_SOUNDMAN_MP3 (0x8660)
|
||||
#define F_OF_SOUNDSTR_MP3 (0x1000)
|
||||
|
||||
#define OF_STARTLOCATION (0x43B348)
|
||||
|
||||
#define F_OF_VIBROCENTRICFONT_V11 (0x143DC0)
|
||||
|
||||
#define OF_LODS_GET_VALUE (0x402385)
|
||||
|
||||
#define OF_PBUBBLE_GET_VALUE0 (0x4FD82C)
|
||||
#define OF_PBUBBLE_GET_VALUE1 (0x4FD839)
|
||||
|
||||
#define OF_ASTEROID_DIST_GET_VALUE (0x520B26)
|
||||
#define OF_AST_BILLBOARD_DIST_GET_VALUE (0x54DD32)
|
||||
|
||||
#define F_OF_BODYPART_DETAILSWITCH_GET_VALUE (0x9EFB2)
|
||||
|
||||
#define OF_SHIP_PREVIEW_WINDOW_SCROLL (0x5E4C14)
|
||||
|
||||
#define OF_FREELANCER_HWND (0x67ECA0)
|
||||
#define OF_FREELANCER_FULLSCREEN_FLAG (0x679BE5)
|
||||
|
||||
#define F_OF_LOAD_SAVE_GAME_CALL (0x69012)
|
||||
#define F_OF_LOAD_SAVE_GAME (0x68D50)
|
||||
#define F_OF_RESTART_NAME_PTR (0x68FDC)
|
||||
#define F_OF_SAVE_FILE_FMT_PTR (0x68FE8)
|
||||
|
||||
#define OF_CHECK_SAVE_GAMES_CALL (0x573BA7)
|
||||
#define OF_CHECK_SAVE_GAMES (0x5A8840)
|
||||
#define OF_SAVE_GAME_FAILED_ERROR_IDS (0x5A88C2)
|
||||
|
||||
#define OF_GET_PLAYER_INSPECT_IMPL (0x54BAF0)
|
||||
#define OF_GET_SHIP_ID (0x4C3E10)
|
||||
|
||||
#define OF_GET_FL_STRING (0x4347E0)
|
||||
#define OF_RESOURCES_HANDLE (0x4347E0)
|
||||
|
||||
#define OF_CURRENT_SYSTEM_ID (0x673354)
|
||||
#define OF_CURRENT_BASE_ID (0x673358)
|
||||
#define OF_CURRENT_SHIP_ID (0x67337C)
|
||||
|
||||
#define OF_GAME_STARTED (0x67A7A4)
|
||||
|
||||
#define OF_INIT_MAIN_MENU (0x5B2BD2)
|
||||
|
||||
#define F_OF_DACOM_VERSION_MS_OFFSET (0x281D + 0xC00)
|
||||
|
||||
#define OF_TOUCHPAD_FIX (0x41FE8F)
|
||||
|
||||
#define CONNECT_TO_SERVER_FUNC (0x5AC710)
|
||||
#define CONNECT_TO_SERVER_THIS (0x67E7B8)
|
||||
|
||||
#define OF_SERVER_LOG_FUNCTION_REF (0x40BDA2 + 1)
|
||||
|
||||
#define OF_MOUSE_Y_UPDATE (0x41FE78)
|
||||
#define OF_MOUSE_X_UPDATE (0x41FE14)
|
||||
|
||||
#define MOUSE_X (*(int*) 0x616840)
|
||||
#define MOUSE_Y (*(int*) 0x616844)
|
||||
|
||||
#define WINDOW_WIDTH (*(int*) 0x679BC8)
|
||||
#define WINDOW_HEIGHT (*(int*) 0x679BCC)
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace patch {
|
||||
|
||||
void detour(unsigned char* pOFunc, void* pHkFunc, unsigned char* originalData, bool jmp)
|
||||
{
|
||||
DWORD dwOldProtection = 0; // Create a DWORD for VirtualProtect calls to allow us to write.
|
||||
BYTE bPatch[5]; // We need to change 5 bytes and I'm going to use memcpy so this is the simplest way.
|
||||
bPatch[0] = jmp ? 0xE9 : 0xE8; // Set the first byte of the byte array to the op code for the JMP or CALL instruction.
|
||||
VirtualProtect((void*)pOFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtection); // Allow us to write to the memory we need to change
|
||||
DWORD dwRelativeAddress = (DWORD)pHkFunc - (DWORD)pOFunc - 5; // Calculate the relative JMP address.
|
||||
memcpy(&bPatch[1], &dwRelativeAddress, 4); // Copy the relative address to the byte array.
|
||||
memcpy(originalData, pOFunc, 5);
|
||||
memcpy(pOFunc, bPatch, 5); // Change the first 5 bytes to the JMP instruction.
|
||||
VirtualProtect((void*)pOFunc, 5, dwOldProtection, &dwOldProtection); // Set the protection back to what it was.
|
||||
}
|
||||
void undetour(unsigned char* pOFunc, unsigned char* originalData)
|
||||
{
|
||||
DWORD dwOldProtection = 0; // Create a DWORD for VirtualProtect calls to allow us to write.
|
||||
VirtualProtect((void*)pOFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtection); // Allow us to write to the memory we need to change
|
||||
memcpy(pOFunc, originalData, 5);
|
||||
VirtualProtect((void*)pOFunc, 5, dwOldProtection, &dwOldProtection); // Set the protection back to what it was.
|
||||
}
|
||||
|
||||
void patch_bytes(unsigned int address, void* pData, unsigned int pSize)
|
||||
{
|
||||
DWORD dwOldProtection = 0;
|
||||
VirtualProtect((void*)address, pSize, PAGE_READWRITE, &dwOldProtection);
|
||||
memcpy((void*)address, pData, pSize);
|
||||
VirtualProtect((void*)address, pSize, dwOldProtection, &dwOldProtection);
|
||||
}
|
||||
|
||||
void set_execute_read_write(unsigned int address, unsigned int size)
|
||||
{
|
||||
DWORD dummy;
|
||||
VirtualProtect((void*)address, size, PAGE_EXECUTE_READWRITE, &dummy);
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
namespace patch {
|
||||
|
||||
void detour(unsigned char* pOFunc, void* pHkFunc, unsigned char* originalData, bool jmp = true);
|
||||
void undetour(unsigned char* pOFunc, unsigned char* originalData);
|
||||
void patch_bytes(unsigned int address, void* pData, unsigned int pSize);
|
||||
void set_execute_read_write(unsigned int address, unsigned int size);
|
||||
|
||||
inline void patch_uint32(unsigned int address, unsigned int data)
|
||||
{
|
||||
patch_bytes(address, (void*)&data, 4);
|
||||
}
|
||||
inline void patch_uint16(unsigned int address, unsigned short data)
|
||||
{
|
||||
patch_bytes(address, (void*)&data, 2);
|
||||
}
|
||||
inline void patch_uint8(unsigned int address, unsigned char data)
|
||||
{
|
||||
patch_bytes(address, (void*)&data, 1);
|
||||
}
|
||||
inline void patch_float(unsigned int address, float data)
|
||||
{
|
||||
patch_bytes(address, (void*)&data, 4);
|
||||
}
|
||||
inline void patch_x3(unsigned int address, unsigned char a, unsigned char b, unsigned char c)
|
||||
{
|
||||
unsigned char bytes[] = { a, b, c };
|
||||
patch_bytes(address, (void*)bytes, 3);
|
||||
}
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#include "restart.h"
|
||||
#include "offsets.h"
|
||||
#include "config.h"
|
||||
#include "patch.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <cstring>
|
||||
|
||||
DWORD loadSaveGameFuncAddr = 0;
|
||||
char restartFileName[64];
|
||||
|
||||
bool __fastcall LoadSaveGame_Hook(PVOID thisptr, PVOID _edx, LPCSTR path, LPCSTR fileName)
|
||||
{
|
||||
if (_stricmp(fileName, restartFileName) == 0)
|
||||
return false;
|
||||
|
||||
typedef bool __fastcall LoadSaveGame(PVOID, PVOID, LPCSTR, LPCSTR);
|
||||
return ((LoadSaveGame*) loadSaveGameFuncAddr)(thisptr, _edx, path, fileName);
|
||||
}
|
||||
|
||||
// Prevents crashes when FL loads a malformed (e.g. from another mod) Restart.fl file.
|
||||
// This code makes it so that Restart.fl is recreated on every restart.
|
||||
bool restart::init()
|
||||
{
|
||||
if (!config::get_config().alwaysregeneraterestartfile)
|
||||
return true;
|
||||
|
||||
auto server = (DWORD) GetModuleHandleA("Server.dll");
|
||||
if (!server)
|
||||
server = (DWORD) LoadLibraryA("Server.dll");
|
||||
if (!server)
|
||||
return false;
|
||||
|
||||
UINT loadSaveGameHookPtr = (UINT) &LoadSaveGame_Hook;
|
||||
UINT loadSaveGameCallAddr = server + F_OF_LOAD_SAVE_GAME_CALL;
|
||||
loadSaveGameFuncAddr = server + F_OF_LOAD_SAVE_GAME;
|
||||
|
||||
patch::set_execute_read_write(server + F_OF_RESTART_NAME_PTR, 4);
|
||||
patch::set_execute_read_write(server + F_OF_SAVE_FILE_FMT_PTR, 4);
|
||||
|
||||
// Dynamically obtain the name of the restart file
|
||||
LPCSTR restartName = *((LPCSTR*) (server + F_OF_RESTART_NAME_PTR));
|
||||
LPCSTR saveFileFmt = *((LPCSTR*) (server + F_OF_SAVE_FILE_FMT_PTR));
|
||||
sprintf_s(restartFileName, sizeof(restartFileName), saveFileFmt, restartName);
|
||||
|
||||
patch::patch_uint32(loadSaveGameCallAddr, loadSaveGameHookPtr - loadSaveGameCallAddr - 4);
|
||||
return true;
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace restart {
|
||||
bool init();
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
#include "savegame.h"
|
||||
#include "config.h"
|
||||
#include "patch.h"
|
||||
#include "log.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <cstring>
|
||||
#include <shlwapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
|
||||
void HandleUserDataPathFail(char * const outputBuffer, char * failedSavesDirectory)
|
||||
{
|
||||
static bool alreadyPrinted = false;
|
||||
if (!alreadyPrinted) {
|
||||
logger::writeformat("flplusplus: failed to access the saves directory for reading and writing (%s). Freelancer may not be able to properly load and store save files.", failedSavesDirectory);
|
||||
alreadyPrinted = true;
|
||||
}
|
||||
|
||||
*outputBuffer = '\0';
|
||||
}
|
||||
|
||||
void WriteSaveDirSuccessMessage(const char* dir)
|
||||
{
|
||||
static bool alreadyPrinted = false;
|
||||
if (!alreadyPrinted) {
|
||||
logger::writeformat("flplusplus: using the following saves directory: \"%s\"", dir);
|
||||
alreadyPrinted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteFallbackMessage()
|
||||
{
|
||||
static bool alreadyPrinted = false;
|
||||
if (!alreadyPrinted) {
|
||||
logger::writeline("flplusplus: saveindirectory option not set but trying to access the root SAVE directory regardless (fallback).");
|
||||
alreadyPrinted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GetSavesInDirectoryPath(char * path)
|
||||
{
|
||||
GetModuleFileNameA(NULL, path, MAX_PATH);
|
||||
PathRemoveFileSpecA(path);
|
||||
PathAppendA(path, "..\\SAVE");
|
||||
}
|
||||
|
||||
bool TryGetMyGamesPath(char * path)
|
||||
{
|
||||
if (SHGetFolderPathA(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path) != S_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PathAppendA(path, "My Games");
|
||||
|
||||
if (_access(path, 6) != 0) {
|
||||
if (_mkdir(path) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PathAppendA(path, config::get_config().savefoldername.c_str());
|
||||
|
||||
if (_access(path, 6) != 0) {
|
||||
if (_mkdir(path) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserDataPath(char * const outputBuffer)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
|
||||
if(config::get_config().saveindirectory) {
|
||||
GetSavesInDirectoryPath(path);
|
||||
} else {
|
||||
if (!TryGetMyGamesPath(path)) {
|
||||
HandleUserDataPathFail(outputBuffer, path);
|
||||
|
||||
// Fallback
|
||||
WriteFallbackMessage();
|
||||
GetSavesInDirectoryPath(path);
|
||||
} else {
|
||||
WriteSaveDirSuccessMessage(path);
|
||||
strcpy_s(outputBuffer, MAX_PATH, path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_access(path, 6) != 0) {
|
||||
if (_mkdir(path) != 0) {
|
||||
HandleUserDataPathFail(outputBuffer, path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
WriteSaveDirSuccessMessage(path);
|
||||
strcpy_s(outputBuffer, MAX_PATH, path);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool savegame::init()
|
||||
{
|
||||
HMODULE common = GetModuleHandleA("common.dll");
|
||||
if (!common)
|
||||
common = LoadLibraryA("common.dll");
|
||||
if (!common)
|
||||
return false;
|
||||
|
||||
auto *origFunc = (unsigned char*)GetProcAddress(common, "?GetUserDataPath@@YA_NQAD@Z");
|
||||
if (!origFunc)
|
||||
return false;
|
||||
|
||||
unsigned char buffer[5];
|
||||
patch::detour(origFunc, (void*)UserDataPath, buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void savegame::get_save_folder(char *buffer)
|
||||
{
|
||||
UserDataPath(buffer);
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace savegame {
|
||||
bool init();
|
||||
void get_save_folder(char *buffer);
|
||||
}
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
//Based off code by Laz
|
||||
|
||||
#include "screenshot.h"
|
||||
#include "patch.h"
|
||||
#include "offsets.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "Freelancer.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <shlwapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <gdiplus.h>
|
||||
#include <wchar.h>
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace Gdiplus;
|
||||
|
||||
#define MAX_SCREENSHOT_PATH_CHECK_ATTEMPTS 20
|
||||
bool altFullscreenScreenshots = false;
|
||||
bool altWindowedScreenshots = false;
|
||||
|
||||
void HandleScreenShotPathFail(char * const outputBuffer, char * failedScreenshotsDirectory)
|
||||
{
|
||||
logger::writeformat(
|
||||
"flplusplus: failed to access the screenshots directory for reading and writing (%s). Freelancer may not be able to properly store screenshots.",
|
||||
failedScreenshotsDirectory);
|
||||
*outputBuffer = '\0';
|
||||
}
|
||||
|
||||
void GetScInDirectoryPath(char * path)
|
||||
{
|
||||
GetModuleFileNameA(NULL, path, MAX_PATH);
|
||||
PathRemoveFileSpecA(path);
|
||||
PathAppendA(path, "..\\SCREENSHOTS");
|
||||
}
|
||||
|
||||
bool TryGetScreenshotsPath(char * path)
|
||||
{
|
||||
if (SHGetFolderPathA(NULL, CSIDL_MYPICTURES | CSIDL_FLAG_CREATE, NULL, 0, path) != S_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PathAppendA(path, config::get_config().screenshotsfoldername.c_str());
|
||||
|
||||
if (_access(path, 6) != 0) {
|
||||
if (_mkdir(path) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScreenShotPath(char * const outputBuffer)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
|
||||
if (config::get_config().screenshotsindirectory) {
|
||||
GetScInDirectoryPath(path);
|
||||
} else {
|
||||
if (!TryGetScreenshotsPath(path)) {
|
||||
HandleScreenShotPathFail(outputBuffer, path);
|
||||
logger::writeline("flplusplus: screenshotsindirectory option not set but trying to access the root SCREENSHOTS directory regardless (fallback).");
|
||||
|
||||
// Fallback
|
||||
GetScInDirectoryPath(path);
|
||||
} else {
|
||||
strcpy_s(outputBuffer, MAX_PATH, path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_access(path, 6) != 0) {
|
||||
if (_mkdir(path) != 0) {
|
||||
HandleScreenShotPathFail(outputBuffer, path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy_s(outputBuffer, MAX_PATH, path);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring stows(const std::string& str)
|
||||
{
|
||||
return std::wstring(str.begin(), str.end());
|
||||
}
|
||||
|
||||
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
|
||||
{
|
||||
UINT num = 0; // number of image encoders
|
||||
UINT size = 0; // size of the image encoder array in bytes
|
||||
|
||||
ImageCodecInfo* pImageCodecInfo = nullptr;
|
||||
|
||||
GetImageEncodersSize(&num, &size);
|
||||
if (size == 0)
|
||||
return -1; // Failure
|
||||
|
||||
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
|
||||
if (pImageCodecInfo == nullptr)
|
||||
return -1; // Failure
|
||||
|
||||
GetImageEncoders(num, size, pImageCodecInfo);
|
||||
|
||||
for (UINT j = 0; j < num; ++j)
|
||||
{
|
||||
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
|
||||
{
|
||||
*pClsid = pImageCodecInfo[j].Clsid;
|
||||
free(pImageCodecInfo);
|
||||
return j; // Success
|
||||
}
|
||||
}
|
||||
free(pImageCodecInfo);
|
||||
return -1; // Failure
|
||||
}
|
||||
|
||||
void GetWindowSize(HWND flHWND, int& width, int& height)
|
||||
{
|
||||
RECT gameWindow;
|
||||
GetClientRect(flHWND, &gameWindow);
|
||||
ClientToScreen(flHWND, (LPPOINT) &gameWindow.left);
|
||||
ClientToScreen(flHWND, (LPPOINT) &gameWindow.right);
|
||||
|
||||
width = gameWindow.right - gameWindow.left;
|
||||
height = gameWindow.bottom - gameWindow.top;
|
||||
}
|
||||
|
||||
std::wstring GetScreenshotOutPath(LPCWSTR directory, const std::wstring &baseFileName, int suffixIndex)
|
||||
{
|
||||
std::wstring fileName = baseFileName;
|
||||
if (suffixIndex > 0) {
|
||||
fileName += std::wstring(L"_") + std::to_wstring(suffixIndex);
|
||||
}
|
||||
fileName += std::wstring(L".png");
|
||||
|
||||
WCHAR cleanedFileName[MAX_PATH];
|
||||
wcscpy_s(cleanedFileName, _countof(cleanedFileName), fileName.c_str());
|
||||
PathCleanupSpec(directory, cleanedFileName);
|
||||
|
||||
return std::wstring(directory) + L'\\' + std::wstring(cleanedFileName);
|
||||
}
|
||||
|
||||
static DWORD OnScreenshot()
|
||||
{
|
||||
char directoryA[MAX_PATH];
|
||||
|
||||
if(!ScreenShotPath(directoryA))
|
||||
{
|
||||
return DWORD(-1);
|
||||
}
|
||||
|
||||
WCHAR directory[MAX_PATH];
|
||||
size_t charsConverted;
|
||||
mbstowcs_s(&charsConverted, directory, _countof(directory), directoryA, _countof(directory) - 1);
|
||||
|
||||
// TODO: Is this check volatile?
|
||||
// What happens if the user turned fullscreen off via a third party app like Borderless Gaming.
|
||||
// Will this be reflected in Freelancer's fullscreen flag?
|
||||
// TODO: Also if fullscreen is true, it's possible that the user moved their window to a secondary monitor and then enabled fullscreen.
|
||||
// Now the FL window is in fullscreen mode on the secondary monitor.
|
||||
// When this happens, the code below will take a screenshot of the main monitor's display which won't show anything from the FL window.
|
||||
// It should actually capture the fullscreen FL window on the secondary monitor.
|
||||
|
||||
// Test if you can move fullscreen window to other monitor and still take a screenshot (windows shift left arrow).
|
||||
// Replace nullptr with GetActiveWindow, or GetForegroundWindow, or GetDesktopWindow, GetWindowDC. Test with broken DxWrapper version from the old FLSR release.
|
||||
bool isFullscreen = (*((PBYTE) OF_FREELANCER_FULLSCREEN_FLAG) & 1) == 1;
|
||||
bool useFullscreenScreenshotCode = isFullscreen;
|
||||
|
||||
if ((isFullscreen && altFullscreenScreenshots) || (!isFullscreen && altWindowedScreenshots))
|
||||
useFullscreenScreenshotCode = !useFullscreenScreenshotCode;
|
||||
|
||||
HWND flHWND = useFullscreenScreenshotCode ? nullptr : *(HWND*) OF_FREELANCER_HWND;
|
||||
|
||||
// get the device context of FL's window
|
||||
HDC hScreenDC = GetDC(flHWND);
|
||||
|
||||
// and a device context to put it in
|
||||
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
|
||||
|
||||
int width, height;
|
||||
|
||||
if (useFullscreenScreenshotCode)
|
||||
{
|
||||
width = GetDeviceCaps(hScreenDC, HORZRES);
|
||||
height = GetDeviceCaps(hScreenDC, VERTRES);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetWindowSize(flHWND, width, height);
|
||||
}
|
||||
|
||||
// maybe worth checking these are positive values
|
||||
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
|
||||
|
||||
// get a new bitmap
|
||||
HBITMAP hOldBitmap = HBITMAP(SelectObject(hMemoryDC, hBitmap));
|
||||
|
||||
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
|
||||
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
|
||||
|
||||
std::time_t rawtime;
|
||||
std::tm* timeinfo;
|
||||
WCHAR buffer[100];
|
||||
|
||||
std::time(&rawtime);
|
||||
timeinfo = std::localtime(&rawtime);
|
||||
|
||||
std::wcsftime(buffer, 80, L"%Y-%m-%d_%H-%M-%S", timeinfo);
|
||||
std::wstring fileName = std::wstring(buffer);
|
||||
|
||||
// If the player starts a game and then goes back to the main menu,
|
||||
// the current system, base, and ship are not always reset.
|
||||
// Hence, this code may obtain the incorrect names if the player is in the main menu.
|
||||
// Therefore, only append the names if a game has started.
|
||||
if (*(bool*) OF_GAME_STARTED)
|
||||
{
|
||||
std::wstring names[] = { GetSystemName(), GetBaseName(), GetShipName() };
|
||||
for (const auto& name : names)
|
||||
{
|
||||
if (!name.empty())
|
||||
fileName += L'_' + name;
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
std::wstring outfile = GetScreenshotOutPath(directory, fileName, i);
|
||||
|
||||
while (PathFileExistsW(outfile.c_str()) && i < MAX_SCREENSHOT_PATH_CHECK_ATTEMPTS) {
|
||||
i++;
|
||||
outfile = GetScreenshotOutPath(directory, fileName, i);
|
||||
}
|
||||
|
||||
if (i < MAX_SCREENSHOT_PATH_CHECK_ATTEMPTS)
|
||||
{
|
||||
GdiplusStartupInput gdiplusStartupInput;
|
||||
ULONG_PTR gdiplusToken;
|
||||
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
|
||||
Bitmap* image = new Bitmap(hBitmap, nullptr);
|
||||
|
||||
CLSID myClsId;
|
||||
GetEncoderClsid(L"image/png", &myClsId);
|
||||
|
||||
Status status = image->Save(outfile.c_str(), &myClsId, nullptr);
|
||||
delete image;
|
||||
|
||||
GdiplusShutdown(gdiplusToken);
|
||||
}
|
||||
|
||||
// clean up
|
||||
DeleteDC(hMemoryDC);
|
||||
ReleaseDC(flHWND, hScreenDC);
|
||||
|
||||
return DWORD(-1);
|
||||
}
|
||||
|
||||
bool screenshot::init_png()
|
||||
{
|
||||
altFullscreenScreenshots = config::get_config().altfullscreenscreenshots;
|
||||
altWindowedScreenshots = config::get_config().altwindowedscreenshots;
|
||||
|
||||
unsigned char buffer[5];
|
||||
patch::detour((unsigned char*)OF_PRINTSCREEN, (void*)OnScreenshot, buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool screenshot::init_path()
|
||||
{
|
||||
HMODULE common = GetModuleHandleA("common.dll");
|
||||
if (!common)
|
||||
common = LoadLibraryA("common.dll");
|
||||
if (!common)
|
||||
return false;
|
||||
|
||||
auto* getScreenShotPath = (unsigned char*)GetProcAddress(common, "?GetScreenShotPath@@YA_NQAD@Z");
|
||||
if (!getScreenShotPath)
|
||||
return false;
|
||||
|
||||
unsigned char buffer[5];
|
||||
patch::detour(getScreenShotPath, (void*)ScreenShotPath, buffer);
|
||||
return true;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace screenshot {
|
||||
bool init_png();
|
||||
bool init_path();
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "shippreviewscroll.h"
|
||||
#include "offsets.h"
|
||||
#include "patch.h"
|
||||
#include "config.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace shippreviewscroll;
|
||||
|
||||
#define MIN_SCROLLING_SPEED 0.0f
|
||||
#define MAX_SCROLLING_SPEED 50.0f
|
||||
|
||||
#define NN_SHIPTRADER_VFTABLE_ADDR (0x5D593C)
|
||||
|
||||
float scrollingSpeed;
|
||||
float scrollMinDistance;
|
||||
float scrollMaxDistance;
|
||||
|
||||
bool __fastcall ShipPreviewWindowScroll(ShipPreviewWindow* window, PVOID _edx, int scrollValue)
|
||||
{
|
||||
// The exact same ship preview element is used in other places as well,
|
||||
// e.g. the FL beta inventory showed a top-down model of the player ship, but plugins can re-enable it.
|
||||
// We don't want the zooming to work anywhere else other than in the ship dealer.
|
||||
if (window->parent->vftable != NN_SHIPTRADER_VFTABLE_ADDR)
|
||||
return false;
|
||||
|
||||
window->zoomLevel += scrollingSpeed * static_cast<float>(scrollValue);
|
||||
|
||||
// Zoom levels are always negative if you "zoom away" from the ship.
|
||||
// If you want to zoom "through" the ship, it becomes positive.
|
||||
window->zoomLevel = std::max<float>(-scrollMaxDistance, std::min<float>(-scrollMinDistance, window->zoomLevel));
|
||||
|
||||
// The scroll function should just return false
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Hook the "on-frame" update function and implement smooth scrolling (see Turret Zoom plugin)
|
||||
// TODO: Allow the scrolling speed to be scaled based on the ship archetype (ini configurable)
|
||||
// [ShipPreviewWindow* +0x32C] contains the ship archetype ID
|
||||
// Alternatively, scale the speed based on the ship class. Array for 3 ships should be in [NN_ShipTrader* + 0x3EC]
|
||||
// Player ship class: [NN_ShipTrader* + 0x3E8]
|
||||
// Alternatively, scale by the ship's radius.
|
||||
// [dalib engine + 0x88] = DALib::Engine::GetRadius(long engineIndex, BYTE unk, float* radius, Vector* vec)
|
||||
// Engine index: [ShipPreviewWindow* +0x4B8]
|
||||
void shippreviewscroll::init()
|
||||
{
|
||||
if (config::get_config().shippreviewscrollingspeed < MIN_SCROLLING_SPEED)
|
||||
scrollingSpeed = MIN_SCROLLING_SPEED;
|
||||
else if (config::get_config().shippreviewscrollingspeed > MAX_SCROLLING_SPEED)
|
||||
scrollingSpeed = MAX_SCROLLING_SPEED;
|
||||
else
|
||||
scrollingSpeed = config::get_config().shippreviewscrollingspeed;
|
||||
|
||||
if (config::get_config().shippreviewscrollinginverse)
|
||||
scrollingSpeed = -scrollingSpeed;
|
||||
|
||||
scrollMinDistance = config::get_config().shippreviewscrollingmindistance;
|
||||
scrollMaxDistance = config::get_config().shippreviewscrollingmaxdistance;
|
||||
|
||||
if (scrollMaxDistance < scrollMinDistance)
|
||||
scrollMaxDistance = scrollMinDistance;
|
||||
|
||||
// Every window in Freelancer has a virtual "scroll" function
|
||||
// In the case of the ship preview window, this function does basically nothing
|
||||
// We replace the pointer to this dummy function in the ship preview window's vftable with our own
|
||||
patch::patch_uint32(OF_SHIP_PREVIEW_WINDOW_SCROLL, (UINT) ShipPreviewWindowScroll);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
namespace shippreviewscroll
|
||||
{
|
||||
struct ShipPreviewParent
|
||||
{
|
||||
DWORD vftable;
|
||||
};
|
||||
|
||||
struct ShipPreviewWindow
|
||||
{
|
||||
DWORD vftable;
|
||||
ShipPreviewParent* parent;
|
||||
BYTE x08[0x3E4];
|
||||
float zoomLevel; // 0x3EC
|
||||
};
|
||||
|
||||
void init();
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#include "startlocation.h"
|
||||
#include "offsets.h"
|
||||
#include "patch.h"
|
||||
#include "config.h"
|
||||
|
||||
void startlocation::init()
|
||||
{
|
||||
if (config::get_config().removestartlocationwarning) {
|
||||
//Patch out "Failed to get start location" warning
|
||||
patch::patch_uint8(OF_STARTLOCATION, 0xEB);
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace startlocation {
|
||||
void init();
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include "startup.h"
|
||||
#include "patch.h"
|
||||
#include "offsets.h"
|
||||
#include "config.h"
|
||||
|
||||
bool startup::check_save_games_hook()
|
||||
{
|
||||
int originalIds = *((int*) OF_SAVE_GAME_FAILED_ERROR_IDS); // Save the original value
|
||||
*((int*) OF_SAVE_GAME_FAILED_ERROR_IDS) = config::get_config().failedtoinitsavesdirids; // Overwrite the original value
|
||||
|
||||
// Call the original function
|
||||
typedef bool (check_save_games)();
|
||||
bool result = ((check_save_games*) OF_CHECK_SAVE_GAMES)();
|
||||
|
||||
*((int*) OF_SAVE_GAME_FAILED_ERROR_IDS) = originalIds; // Restore the original value
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void startup::init()
|
||||
{
|
||||
BYTE originalBytes[5];
|
||||
|
||||
patch::set_execute_read_write(OF_SAVE_GAME_FAILED_ERROR_IDS, sizeof(UINT));
|
||||
patch::detour((unsigned char*) OF_CHECK_SAVE_GAMES_CALL, (void*) startup::check_save_games_hook, originalBytes, false);
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
namespace startup {
|
||||
void init();
|
||||
bool check_save_games_hook();
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
#include "thnplayer.h"
|
||||
#include "savegame.h"
|
||||
#include "log.h"
|
||||
#include "patch.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <flplusplus.h>
|
||||
#include <stdlib.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
char *ScriptOverride = NULL;
|
||||
|
||||
static bool ParseArguments(LPWSTR* args, int numArgs, LPWSTR* ret)
|
||||
{
|
||||
for(int i = 0; i < numArgs; i++) {
|
||||
if(wcscmp(args[i], L"-thn") == 0) {
|
||||
if(i + 1 < numArgs && PathFileExistsW(args[i + 1])) {
|
||||
*ret = _wcsdup(args[i + 1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned char MenuButtonsRemove[] = {
|
||||
0xE8, 0x47, 0xE5, 0xEA, 0xFF, 0x83, 0x7C, 0x24, 0x50,
|
||||
0x04, 0x77, 0x11, 0xDB, 0x05, 0x4C, 0x46, 0x57, 0x00,
|
||||
0xEB, 0x0D
|
||||
};
|
||||
|
||||
typedef bool (__fastcall *pBExit)(void*);
|
||||
typedef bool (__fastcall *pBEnter)(void*,int,UINT);
|
||||
typedef void (__cdecl *pUpdateTime)(double delta);
|
||||
typedef void(__cdecl *pFlushConsole)(int ident, char* data, DWORD len);
|
||||
|
||||
void* FL = (void*)0x668708;
|
||||
static pBEnter FL_BaseEnter = (pBEnter)0x43b290;
|
||||
static pBExit FL_BaseExit = (pBExit)0x43b3e0;
|
||||
static pUpdateTime UpdateTime;
|
||||
static pFlushConsole FlushConsole = (pFlushConsole)0x46A150;
|
||||
|
||||
static unsigned char thornLoadData[5];
|
||||
typedef void *(__cdecl *ScriptLoadPtr)(const char*);
|
||||
static ScriptLoadPtr _ThornScriptLoad;
|
||||
|
||||
static bool firstLoad = true;
|
||||
|
||||
struct Chat {
|
||||
UINT Type1;
|
||||
UINT Len1;
|
||||
UINT Mask;
|
||||
UINT Data;
|
||||
UINT Type2;
|
||||
UINT Len2;
|
||||
wchar_t Message[1024];
|
||||
};
|
||||
|
||||
static void PrintText(const wchar_t *text)
|
||||
{
|
||||
Chat chat;
|
||||
memset(&chat, 0, sizeof(Chat));
|
||||
chat.Type1 = 0x1;
|
||||
chat.Len1 = 0x8;
|
||||
chat.Mask = 0xFFFFFF00;
|
||||
chat.Data = 0xFFFFFF00;
|
||||
chat.Type2 = 0x2;
|
||||
chat.Len2 = wcslen(text) * 2 + 2;
|
||||
wcscpy(chat.Message, text);
|
||||
FlushConsole(0, (char*)&chat, chat.Len2 + 24);
|
||||
}
|
||||
|
||||
|
||||
void * __cdecl OnThornLoad(const char *script)
|
||||
{
|
||||
patch::undetour((unsigned char*)_ThornScriptLoad, thornLoadData);
|
||||
logger::writeline(ScriptOverride ? (const char*)ScriptOverride : script);
|
||||
void* retval = _ThornScriptLoad(ScriptOverride ? (const char*)ScriptOverride : script);
|
||||
patch::detour((unsigned char*)_ThornScriptLoad, (void*)OnThornLoad, thornLoadData);
|
||||
if(firstLoad) {
|
||||
PrintText(L"THN Player");
|
||||
PrintText(L"F5 - Refresh");
|
||||
PrintText(L"F9 - Open");
|
||||
PrintText(L"Esc - Exit");
|
||||
firstLoad = false;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void SceneReload()
|
||||
{
|
||||
FL_BaseExit(FL);
|
||||
FL_BaseEnter(FL, 0, 0xA3BC3888); //intro1_base
|
||||
}
|
||||
|
||||
static int reloadFrames = -1;
|
||||
static void OpenFile()
|
||||
{
|
||||
OPENFILENAME ofn; // common dialog box structure
|
||||
char szFile[260]; // buffer for file name
|
||||
|
||||
// Initialize OPENFILENAME
|
||||
ZeroMemory(&ofn, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.hwndOwner = NULL;
|
||||
ofn.lpstrFile = szFile;
|
||||
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
|
||||
// use the contents of szFile to initialize itself.
|
||||
ofn.lpstrFile[0] = '\0';
|
||||
ofn.nMaxFile = sizeof(szFile);
|
||||
ofn.lpstrFilter = "THN\0*.THN;*.LUA\0All\0*.*\0";
|
||||
ofn.nFilterIndex = 0;
|
||||
ofn.lpstrFileTitle = NULL;
|
||||
ofn.nMaxFileTitle = 0;
|
||||
ofn.lpstrInitialDir = NULL;
|
||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
|
||||
|
||||
// Display the Open dialog box.
|
||||
if (GetOpenFileName(&ofn)==TRUE) {
|
||||
logger::writeline("Opening");
|
||||
logger::writeline(szFile);
|
||||
ScriptOverride = _strdup(szFile);
|
||||
reloadFrames = 4;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char updateData[5];
|
||||
|
||||
static void Update(const double delta)
|
||||
{
|
||||
if((GetAsyncKeyState(VK_F5) & 1)) {
|
||||
SceneReload();
|
||||
}
|
||||
if((GetAsyncKeyState(VK_F9) & 1)) {
|
||||
OpenFile();
|
||||
}
|
||||
// Skip hitching
|
||||
if(reloadFrames > 0) {
|
||||
reloadFrames--;
|
||||
} else if(reloadFrames == 0) {
|
||||
reloadFrames = -1;
|
||||
SceneReload();
|
||||
}
|
||||
patch::undetour((unsigned char*)UpdateTime, updateData);
|
||||
UpdateTime(delta);
|
||||
patch::detour((unsigned char*)UpdateTime, (void*)Update, updateData);
|
||||
}
|
||||
|
||||
bool thnplayer::init()
|
||||
{
|
||||
int numArgs;
|
||||
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &numArgs);
|
||||
if (!args)
|
||||
return false;
|
||||
|
||||
LPWSTR thnscript = NULL;
|
||||
if(ParseArguments(args, numArgs, &thnscript)) {
|
||||
HMODULE freelancer = GetModuleHandleA("freelancer.exe");
|
||||
HMODULE common = GetModuleHandleA("common.dll");
|
||||
if (!common)
|
||||
common = LoadLibraryA("common.dll");
|
||||
|
||||
pUpdateTime updateTime = common
|
||||
? (pUpdateTime)GetProcAddress(common, "?UpdateGlobalTime@Timing@@YAXN@Z")
|
||||
: nullptr;
|
||||
ScriptLoadPtr thornScriptLoad = common
|
||||
? (ScriptLoadPtr)GetProcAddress(common, "?ThornScriptLoad@@YAPAUIScriptEngine@@PBD@Z")
|
||||
: nullptr;
|
||||
|
||||
if (!freelancer || !updateTime || !thornScriptLoad) {
|
||||
free((void*)thnscript);
|
||||
LocalFree(args);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(thnscript) {
|
||||
char buffer[500];
|
||||
wcstombs(buffer, thnscript, 500);
|
||||
free((void*)thnscript);
|
||||
ScriptOverride = _strdup(buffer);
|
||||
}
|
||||
DWORD FL = (DWORD)freelancer;
|
||||
//Remove all menu buttons
|
||||
patch::patch_bytes(FL + 0x174634, (void*)MenuButtonsRemove, 20);
|
||||
patch::patch_uint16(FL + 0x1746CA, 0x0);
|
||||
patch::patch_uint16(FL + 0x174707, 0x0);
|
||||
patch::patch_uint16(FL + 0x174744, 0x0);
|
||||
patch::patch_uint16(FL + 0x174781, 0x0);
|
||||
patch::patch_uint16(FL + 0x1747BE, 0x0);
|
||||
patch::patch_bytes(FL + 0x1E23DC, (void*)"null", 5); //Disable ui_motion_swish
|
||||
//Remove version text
|
||||
patch::patch_uint32(FL + 0x16DDEC, 0x1);
|
||||
patch::patch_uint32(FL + 0x174890, 0x1);
|
||||
//Remove logo
|
||||
patch::patch_uint32(FL + 0x1E266C, 0x0);
|
||||
//Persistent text
|
||||
patch::patch_uint32(FL + 0x0691CA, 0x7FFFFFFE);
|
||||
//Hook update
|
||||
UpdateTime = updateTime;
|
||||
patch::detour((unsigned char*)UpdateTime, (void*)Update, updateData);
|
||||
//Permanent hook load function
|
||||
_ThornScriptLoad = thornScriptLoad;
|
||||
patch::detour((unsigned char*)_ThornScriptLoad, (void*)OnThornLoad, thornLoadData);
|
||||
}
|
||||
LocalFree(args);
|
||||
return true;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
namespace thnplayer {
|
||||
bool init();
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#include "touchpad.h"
|
||||
#include "offsets.h"
|
||||
#include "patch.h"
|
||||
#include "config.h"
|
||||
|
||||
// TODO: There's a bug in FL where if you play the game on a system that has a touchpad (laptop for instance),
|
||||
// then scrolling will not work at all. If you use that touchpad to scroll, then it appears that you scroll infinitely.
|
||||
// When this happens, toggling engine kill doesn't seem to work anymore either.
|
||||
// This function applies a patch that makes scrolling with a touchpad behave as you'd expect.
|
||||
// However, it completely breaks normal mouse wheel scrolling.
|
||||
// It'd be nice if a solution could be implemented that fixes touchpad scrolling without breaking mouse wheel scrolling.
|
||||
void touchpad::init()
|
||||
{
|
||||
if (config::get_config().touchpadsupport) {
|
||||
// Fix touchpad scrolling but break normal mouse wheel scrolling
|
||||
patch::patch_uint8(OF_TOUCHPAD_FIX, 0x00);
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace touchpad {
|
||||
void init();
|
||||
}
|
||||
Reference in New Issue
Block a user