[PATCH] Hack for lotro
Daniel Santos
daniel.santos at pobox.com
Tue Jul 15 19:11:53 CDT 2008
---
dlls/ws2_32/Makefile.in | 3 +-
dlls/ws2_32/lotro_hacks.c | 342 +++++++++++++++++++++++++++++++++++++++++++++
dlls/ws2_32/lotro_hacks.h | 57 ++++++++
dlls/ws2_32/socket.c | 20 +++
4 files changed, 421 insertions(+), 1 deletions(-)
create mode 100644 dlls/ws2_32/lotro_hacks.c
create mode 100644 dlls/ws2_32/lotro_hacks.h
diff --git a/dlls/ws2_32/Makefile.in b/dlls/ws2_32/Makefile.in
index 1bb424f..7aa61e9 100644
--- a/dlls/ws2_32/Makefile.in
+++ b/dlls/ws2_32/Makefile.in
@@ -12,7 +12,8 @@ EXTRALIBS = @LIBPOLL@
C_SRCS = \
async.c \
protocol.c \
- socket.c
+ socket.c \
+ lotro_hack.c
C_SRCS16 = socket16.c
diff --git a/dlls/ws2_32/lotro_hacks.c b/dlls/ws2_32/lotro_hacks.c
new file mode 100644
index 0000000..2fc4537
--- /dev/null
+++ b/dlls/ws2_32/lotro_hacks.c
@@ -0,0 +1,342 @@
+/*
+ * implementation file for hacks to work around bugs in Lord of the Rings Online
+ * client. See http://bugs.winehq.org/show_bug.cgi?id=12302 for more info.
+ *
+ * Copyright (C) 2008 Daniel Santos <daniel.santos at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_IPC_H
+# include <sys/ipc.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+#endif
+
+#if defined(__EMX__)
+# include <sys/so_ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_MSG_H
+# include <sys/msg.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+# include <netinet/tcp.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
+#ifdef HAVE_NETIPX_IPX_H
+# include <netipx/ipx.h>
+# define HAVE_IPX
+#elif defined(HAVE_LINUX_IPX_H)
+# ifdef HAVE_ASM_TYPES_H
+# include <asm/types.h>
+# endif
+# include <linux/ipx.h>
+# define HAVE_IPX
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winnls.h"
+#include "winsock2.h"
+#include "mswsock.h"
+#include "ws2tcpip.h"
+#include "ws2spi.h"
+#include "wsipx.h"
+#include "winnt.h"
+#include "iphlpapi.h"
+#include "wine/server.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "lotro_hacks.h"
+#include "winreg.h"
+WINE_DEFAULT_DEBUG_CHANNEL(lotro_hack);
+
+BOOL _lotro_hack_enabled = FALSE;
+ws_lotro_hack_settings _lotro_hack_settings;
+
+/**
+ * Frees resources allocated for a WS_lotro_sent_msg structure and marks it as
+ * unallocated
+ */
+static inline void _lotro_hack_free_msg(ws_lotro_hack_sent_msg *msg) {
+ msg->valid = 0;
+ HeapFree( GetProcessHeap(), 0, msg->data.buf );
+}
+
+/**
+ * Copies data from a packet currently being sent into a message buffer for
+ * later comparison
+ */
+static inline void _lotro_hack_copy_msg(ws_lotro_hack_sent_msg *dest,
+ SOCKET s, LPWSABUF data_buf, const struct WS_sockaddr_in *to) {
+ dest->valid = 1;
+ dest->first_sent = clock();
+ dest->hits = 0;
+ dest->s = s;
+ memcpy( &dest->to, to, sizeof(struct WS_sockaddr_in) );
+ dest->data.len = data_buf->len;
+ dest->data.buf = HeapAlloc( GetProcessHeap(), 0, dest->data.len );
+ memcpy( dest->data.buf, data_buf->buf, data_buf->len );
+}
+
+/**
+ * Compares the packet currently being sent with one that was previously sent
+ */
+static inline int _lotro_hack_compare_msg(ws_lotro_hack_sent_msg *msg,
+ SOCKET s, LPWSABUF data_buf, const struct WS_sockaddr_in *to) {
+ int i;
+
+ if(msg->s != s
+ || msg->data.len != data_buf->len
+ || msg->to.sin_port != to->sin_port
+ || msg->to.sin_addr.S_un.S_addr != to->sin_addr.S_un.S_addr) {
+ return 1;
+ }
+
+ for(i = 0; i < data_buf->len; ++i) {
+ if(msg->data.buf[i] != data_buf->buf[i]) {
+ return 1;
+ }
+ }
+ return 0;
+
+/* alternate mechanism
+ char *p1, *p2, *p1end;
+ p1end = &msg->data.buf[msg->data.len];
+ for(p1 = msg->data.buf, p2 = data_buf->buf; p1 < p1end; ++p1, ++p2) {
+ if(*p1 != *p2) {
+ return 1;
+ }
+ }
+*/
+}
+
+/** Retrieves a DWORD value from the registry */
+static inline BOOL _lotro_hack_get_reg_value(HKEY hkKey, const char* name,
+ DWORD *value) {
+ DWORD valueSize = sizeof(DWORD);
+ LONG ret = RegGetValueA(
+ hkKey, NULL, name, RRF_RT_REG_DWORD, NULL, value, &valueSize);
+ if(ret != ERROR_SUCCESS) {
+ ERR("in _lotro_hack_init_global() call to RegGetValue(hkLotro, NULL, "
+ "\"%s\", ...) failed with %d.\n", name, ret);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/** Performs per-process initialization */
+void _lotro_hack_init_global() {
+ LONG ret;
+ HKEY hkLotro;
+ DWORD value, cache_dur_ms, cache_ulen;
+ DWORD valueSize = sizeof(value);
+ ret = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\Hacks\\LOTRO", 0,
+ KEY_READ, &hkLotro);
+ if(ret == ERROR_FILE_NOT_FOUND) {
+ return;
+ }
+ if(ret != ERROR_SUCCESS) {
+ ERR("in _lotro_hack_init_global() call to RegOpenKeyEx("
+ "HKEY_CURRENT_USER, \"Software\\Wine\\Hacks\\LOTRO\"...) "
+ "failed with %d.\n", ret);
+ return;
+ }
+
+ ret = RegGetValueA(hkLotro, NULL, "Enabled", RRF_RT_REG_DWORD, NULL, &value,
+ &valueSize);
+ if(ret == ERROR_FILE_NOT_FOUND) {
+ RegCloseKey(hkLotro);
+ return;
+ }
+ if(ret != ERROR_SUCCESS) {
+ ERR("in _lotro_hack_init_global() call to RegGetValue() "
+ "failed with %d.\n", ret);
+ RegCloseKey(hkLotro);
+ return;
+ }
+
+ if(!( _lotro_hack_get_reg_value(hkLotro, "PacketCacheDuration",
+ &cache_dur_ms)
+ || _lotro_hack_get_reg_value(hkLotro, "PacketCacheLength",
+ &cache_ulen)
+ || _lotro_hack_get_reg_value(hkLotro, "PacketMaxDupes",
+ &_lotro_hack_settings.max_dupes))) {
+ _lotro_hack_settings.cache_dur = cache_dur_ms * (CLOCKS_PER_SEC / 1000);
+ _lotro_hack_settings.cache_len = (int)cache_ulen;
+ MESSAGE("*** Lord of the Rings Online hacks enabled. ***\n");
+ _lotro_hack_enabled = TRUE;
+ }
+ RegCloseKey(hkLotro);
+}
+
+/** Initializes structure used for this hack (per-thread initialization) */
+void _lotro_hack_init_thread(ws_lotro_hack_sent_msgs *lotro_msgs) {
+ lotro_msgs->last_entry = -1;
+ lotro_msgs->msgs = (ws_lotro_hack_sent_msg*) HeapAlloc(
+ GetProcessHeap(), 0,
+ sizeof(ws_lotro_hack_sent_msg) * _lotro_hack_settings.cache_len );
+}
+
+/** Frees per-thread data allocated for the purposes of this hack. */
+void _lotro_hack_free_thread(ws_lotro_hack_sent_msgs *lotro_msgs) {
+ int i;
+ for(i = 0; i <= lotro_msgs->last_entry; ++i) {
+ if(lotro_msgs->msgs[i].valid) {
+ _lotro_hack_free_msg(&lotro_msgs->msgs[i]);
+ }
+ }
+}
+
+/**
+ * Performs hack. Returns non-zero if the packet should be dropped, zero
+ * otherwise.
+ */
+int _lotro_hack(ws_lotro_hack_sent_msgs *lotro_msgs, SOCKET s,
+ LPWSABUF data_buf, const struct WS_sockaddr_in *to) {
+ clock_t cur_time;
+ clock_t earliest_buf_time;
+ int last_entry;
+ int next_avail;
+ int i;
+
+ cur_time = clock();
+ earliest_buf_time = cur_time - _lotro_hack_settings.cache_dur;
+ last_entry = -1; /* -1 indicates unknown */
+ next_avail = _lotro_hack_settings.cache_len; /* indicates none known */
+
+ for(i = 0; i <= lotro_msgs->last_entry; ++i) {
+ ws_lotro_hack_sent_msg *msg = &lotro_msgs->msgs[i];
+
+ /* check for old messages and discard them, correcting last_entry as
+ needed */
+ if(msg->valid) {
+ if(msg->first_sent < earliest_buf_time) {
+ TRACE("purging old msg, thread=0x%08x, slot=%d\n",
+ GetCurrentThreadId(), i);
+ _lotro_hack_free_msg(msg);
+ if(next_avail > i) {
+ next_avail = i;
+ }
+ continue;
+ }
+ last_entry = i;
+
+ /* if we match and the max hit count is exceeded, we drop the
+ packet */
+ if(!_lotro_hack_compare_msg(msg, s, data_buf, to)) {
+ TRACE("hit, thread=0x%08x, slot=%d, hits=%d\n",
+ GetCurrentThreadId(), i, msg->hits);
+ return msg->hits++ > _lotro_hack_settings.max_dupes;
+ }
+ } else {
+ if(next_avail > i) {
+ next_avail = i;
+ }
+ }
+ }
+
+ /* if we make it here, we're seeing a new packet and we should record it. */
+
+ if(next_avail == _lotro_hack_settings.cache_len) {
+ if(last_entry == _lotro_hack_settings.cache_len - 1) {
+ TRACE("out of slots, thread=0x%08x\n", GetCurrentThreadId());
+ return 0;
+ }
+ next_avail = ++last_entry;
+ }
+
+ TRACE("caching msg, thread=0x%08x, slot=%d\n", GetCurrentThreadId(),
+ next_avail);
+ _lotro_hack_copy_msg(&lotro_msgs->msgs[next_avail], s, data_buf, to);
+ lotro_msgs->last_entry = last_entry;
+
+ return 0;
+}
+
diff --git a/dlls/ws2_32/lotro_hacks.h b/dlls/ws2_32/lotro_hacks.h
new file mode 100644
index 0000000..bb50e77
--- /dev/null
+++ b/dlls/ws2_32/lotro_hacks.h
@@ -0,0 +1,57 @@
+/*
+ * header file for hacks to work around bugs in Lord of the Rings Online client
+ *
+ * Copyright (C) 2008 Daniel Santos <daniel.santos at pobox.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#ifndef _LOTRO_HACKS_
+#define _LOTRO_HACKS_
+
+#include "winsock2.h"
+
+/** Global settings for Lord of the Rings Online bug countermeasures. */
+typedef struct {
+ DWORD cache_dur;
+ int cache_len;
+ DWORD max_dupes;
+} ws_lotro_hack_settings;
+
+typedef struct {
+ int valid;
+ clock_t first_sent;
+ int hits;
+ SOCKET s;
+ struct WS_sockaddr_in to;
+ WSABUF data;
+} ws_lotro_hack_sent_msg;
+
+typedef struct {
+ int last_entry;
+ ws_lotro_hack_sent_msg *msgs;
+} ws_lotro_hack_sent_msgs;
+
+extern BOOL _lotro_hack_enabled;
+extern ws_lotro_hack_settings _lotro_hack_settings;
+
+void _lotro_hack_init_global();
+void _lotro_hack_init_thread(ws_lotro_hack_sent_msgs *lotro_msgs);
+void _lotro_hack_free_thread(ws_lotro_hack_sent_msgs *lotro_msgs);
+int _lotro_hack(ws_lotro_hack_sent_msgs *lotro_msgs, SOCKET s, LPWSABUF data_buf,
+ const struct WS_sockaddr_in *to);
+
+#endif // _LOTRO_HACKS_
\ No newline at end of file
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 6624c96..7da24c7 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -152,6 +152,8 @@
#define INADDR_NONE ~0UL
#endif
+#include "lotro_hacks.h"
+
WINE_DEFAULT_DEBUG_CHANNEL(winsock);
/* critical section to protect some non-reentrant net function */
@@ -227,6 +229,7 @@ struct per_thread_data
int he_len;
int se_len;
int pe_len;
+ ws_lotro_hack_sent_msgs lotro_msgs;
};
static INT num_startup; /* reference counter */
@@ -491,6 +494,7 @@ static struct per_thread_data *get_per_thread_data(void)
{
ptb = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ptb) );
NtCurrentTeb()->WinSockData = ptb;
+ _lotro_hack_init_thread(&ptb->lotro_msgs);
}
return ptb;
}
@@ -509,6 +513,9 @@ static void free_per_thread_data(void)
ptb->se_buffer = NULL;
ptb->pe_buffer = NULL;
+ /* free lotro hack buffer */
+ _lotro_hack_free_thread(&ptb->lotro_msgs);
+
HeapFree( GetProcessHeap(), 0, ptb );
NtCurrentTeb()->WinSockData = NULL;
}
@@ -521,6 +528,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
TRACE("%p 0x%x %p\n", hInstDLL, fdwReason, fImpLoad);
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
+ _lotro_hack_init_global();
break;
case DLL_PROCESS_DETACH:
free_per_thread_data();
@@ -2679,6 +2687,18 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
return SOCKET_ERROR;
}
+ if(_lotro_hack_enabled
+ && to->sa_family == AF_INET
+ && dwBufferCount == 1
+ && _lotro_hack(&get_per_thread_data()->lotro_msgs,
+ s, lpBuffers, (struct WS_sockaddr_in*)to)) {
+ Sleep(10);
+ if(lpNumberOfBytesSent) {
+ *lpNumberOfBytesSent = lpBuffers->len;
+ }
+ return 0;
+ }
+
fd = get_sock_fd( s, FILE_WRITE_DATA, &options );
TRACE( "fd=%d, options=%x\n", fd, options );
--
1.5.6.1
--------------090402080503030308010504--
More information about the wine-patches
mailing list