[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