#!/usr/bin/python

"""
No WON patch for Half-Life dedicated Linux servers.
Works for engine_XXX.so (including engine_amd64.so!) from latest
(as-of February 2005) Linux Server Engine version 16.
Not tested on earlier versions.

Technical notes: the routine in question is:

00290e80 <_ZNK8netadr_s16CompareClassBAdrERKS_>:
  290e80:       53                      push   %ebx
  290e81:       ba 00 00 00 00          mov    $0x0,%edx
  290e86:       8b 5c 24 0c             mov    0xc(%esp),%ebx
  290e8a:       8b 4c 24 08             mov    0x8(%esp),%ecx
  290e8e:       8b 03                   mov    (%ebx),%eax
  290e90:       3b 01                   cmp    (%ecx),%eax
  290e92:       74 0c                   je     290ea0 <_ZNK8netadr_s16CompareClassBAdrERKS
  290e94:       5b                      pop    %ebx
  290e95:       89 d0                   mov    %edx,%eax
  290e97:       c3                      ret
  290e98:       90                      nop
  290e99:       8d b4 26 00 00 00 00    lea    0x0(%esi),%esi
  290ea0:       83 f8 01                cmp    $0x1,%eax
  290ea3:       b2 01                   mov    $0x1,%dl
  290ea5:       74 ed                   je     290e94 <_ZNK8netadr_s16CompareClassBAdrERKS
  290ea7:       83 f8 03                cmp    $0x3,%eax
  290eaa:       74 09                   je     290eb5 <_ZNK8netadr_s16CompareClassBAdrERKS
  290eac:       ba 00 00 00 00          mov    $0x0,%edx
  290eb1:       5b                      pop    %ebx
  290eb2:       89 d0                   mov    %edx,%eax
  290eb4:       c3                      ret
  290eb5:       66 8b 41 04             mov    0x4(%ecx),%ax
  290eb9:       66 39 43 04             cmp    %ax,0x4(%ebx)
  290ebd:       74 d5                   je     290e94 <_ZNK8netadr_s16CompareClassBAdrERKS
  290ebf:       ba 00 00 00 00          mov    $0x0,%edx
  290ec4:       eb eb                   jmp    290eb1 <_ZNK8netadr_s16CompareClassBAdrERKS

If it returns 1, the player is admited no matter whethere it's in a
class C network or not. So what we have to do is to fix this routine:

		b8 01 00 00 00       	mov    eax,0x1
		c3                   	ret

This script requires the `objdump' program. If you don't have it installed,
you must install the binutils package.
"""

import os
import sys
import shutil

def patch_str (contents, times, findstr, replstr):
    print "	Applying the %s patch:" % findstr,
    for x in range(times):
        patch_ofs = contents.find (findstr)
        if (patch_ofs == -1):
            print "Failed",
        else:
            contents = contents [:patch_ofs] + replstr + contents [patch_ofs + len (replstr):]
            print "Success",
    print
    return contents

def patch (fn):
    print "Patching file `%s' ..." % fn

    # The Right Sequence {tm}
    seq = "\xb8\x01\x00\x00\x00\xc3";

    x = os.popen ("objdump -t '" + fn + "' | grep _ZNK8netadr_s16CompareClassBAdrERKS_");
    line = x.readline (1000)
    if (not line):
	print "	Failed to find function CompareClassBAdr in '" + fn + "'"
	return

    patch_ofs = int (line.split (None, 1) [0], 16)
    x.close ();

    x = open (fn, "r+");
    x.seek (patch_ofs, 0);
    line = x.read (len (seq));
    if (line == seq):
	print "	File", fn, "is already patched"
    else:
	shutil.copyfile (fn, fn + ".orig")
	# Now read the whole file, since we have to perform some searches on it
	x.seek (0, 0)
	contents = x.read (99999999)

	# Apply the first patch
        contents = contents [:patch_ofs] + seq + contents [patch_ofs + len(seq):]
	print "	Applying the NO-WON patch: Success"

	x.seek (0, 0)
	x.write (contents)

    x.close ();

if (len (sys.argv) < 2):
    print "Usage: " + sys.argv[0] + " {engine*.so}"
    sys.exit (-1)

for i in range(1, len(sys.argv)):
    patch (sys.argv [i])

