• Welcome to Valhalla Legends Archive.
 
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - lord2800

#1
Quote from: Trunning on May 11, 2010, 01:12 PM
Can I get an example on how to convert the DWORD IP to an actual IP Address?

307491135 is the DWORD

3f f1 53 12 are the 4 bytes

63.241.83.18 is the actual IP

Each byte is one octet of the IP address. sprintf("%d.%d.%d.%d", byte[0] byte[1], byte[2], byte[3]); would do the trick.
#2
Quote from: l)ragon on May 08, 2010, 02:13 AMPvPGN is for failures, only way for you to get real results is by using and abusing the real system.

Of course you don't use PvPGN and say your code works, but it sure does make getting the BNCS sequence down easier. It saved me hours of waiting every time I screwed something innocuous up that took me 30 seconds to fix (and had to wait 5 minutes to test again).
#3
Quote from: Trunning on May 08, 2010, 01:20 AM
Damn I'm good, IPBanned from all four servers, ah well, this will a painfully long trial and error process.

PvPGN helps a lot when testing. Especially setting up a local instance.
#4
I got bored and decided to implement this whole thing, and here's what I came up with:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Emit;
using System.Reflection;

namespace BNet
{
class CheckRevision
{
private static Dictionary<int, OpCode> Ldargs = new Dictionary<int, OpCode>() {
{0, OpCodes.Ldarg_0}, {1, OpCodes.Ldarg_1}, {2, OpCodes.Ldarg_2}, {3, OpCodes.Ldarg_3}
};

private static Dictionary<char, OpCode> Operators = new Dictionary<char, OpCode>() {
{'+', OpCodes.Add}, {'-', OpCodes.Sub}, {'*', OpCodes.Mul}, {'/', OpCodes.Div},
{'|', OpCodes.Or},  {'&', OpCodes.And}, {'^', OpCodes.Xor}
};

private delegate void FileHasher(ref uint a, ref uint b, ref uint c, ref uint s, byte[] f);

private static uint[] hashes = new uint[] {0xE7F4CB62, 0xF6A14FFC, 0xAA5504AF, 0x871FCDC2, 0x11BF6A18, 0xC57292E6, 0x7927D27E, 0x2FEC8733};

public static uint ComputeHash(string formula, string mpqFile, FileStream gameExe, FileStream bnclientDll, FileStream d2clientDll)
{
byte[] game = File.ReadAllBytes(gameExe.Name);
byte[] bnclient = File.ReadAllBytes(bnclientDll.Name);
byte[] d2client = File.ReadAllBytes(d2clientDll.Name);

int mpq = Convert.ToInt32(mpqFile[mpqFile.LastIndexOf('.') - 1].ToString(), 10);
uint[] values = new uint[4];
IEnumerable<FormulaOp> ops = BuildFormula(formula, ref values);

// UGLY HACK: use the hardcoded mpq seed
// TODO: figure out how to get the real mpq seed
values[0] ^= hashes[mpq];

FileHasher HashFile = BuildFileHasher(ops);

HashFile(ref values[0], ref values[1], ref values[2], ref values[3], game);
HashFile(ref values[0], ref values[1], ref values[2], ref values[3], bnclient);
HashFile(ref values[0], ref values[1], ref values[2], ref values[3], d2client);

return values[2];
}

private static FileHasher BuildFileHasher(IEnumerable<FormulaOp> ops)
{
Type uint_t = typeof(uint).MakeByRefType();
DynamicMethod method = new DynamicMethod("HashFile", typeof(void),
new Type[] { uint_t, uint_t, uint_t, uint_t, typeof(byte[]) });
MethodInfo touint32 = typeof(BitConverter).GetMethod("ToUInt32");

ILGenerator gen = method.GetILGenerator();

Label start = gen.DefineLabel();
LocalBuilder index = gen.DeclareLocal(typeof(int));
LocalBuilder len = gen.DeclareLocal(typeof(int));

// initialize the loop counter
gen.Emit(OpCodes.Ldc_I4_0);
gen.Emit(OpCodes.Stloc, index);

// load the length of the array into a local
gen.Emit(OpCodes.Ldarg, (short)4);
gen.Emit(OpCodes.Ldlen);
gen.Emit(OpCodes.Conv_I4);
gen.Emit(OpCodes.Stloc, len);

// start of loop across the file
gen.MarkLabel(start);

// load the value of arg4 at index into the address of arg3
gen.Emit(OpCodes.Ldarg_3);
gen.Emit(OpCodes.Ldarg_S, (byte)4);
gen.Emit(OpCodes.Ldloc, index);
gen.EmitCall(OpCodes.Call, touint32, null);
gen.Emit(OpCodes.Stind_I4);

// for each op in the formula...
foreach(var op in ops)
{
// load the result address
gen.Emit(Ldargs[op.Result]);

// load the first value
gen.Emit(Ldargs[op.Variable1]);
gen.Emit(OpCodes.Ldind_U4);

// load the second value
gen.Emit(Ldargs[op.Variable2]);
gen.Emit(OpCodes.Ldind_U4);

// execute the operator
gen.Emit(Operators[op.Operation]);

// store the result in the result address
gen.Emit(OpCodes.Stind_I4);
}

// increment the loop counter
gen.Emit(OpCodes.Ldloc, index);
gen.Emit(OpCodes.Ldc_I4_4);
gen.Emit(OpCodes.Add);
gen.Emit(OpCodes.Stloc, index);

// jump back to the top of the label if the loop counter is less arg4's length
gen.Emit(OpCodes.Ldloc, index);
gen.Emit(OpCodes.Ldloc, len);
gen.Emit(OpCodes.Blt, start);
gen.Emit(OpCodes.Ret);

FileHasher del = (FileHasher)method.CreateDelegate(typeof(FileHasher));
return del;
}

private static IEnumerable<FormulaOp> BuildFormula(string formula, ref uint[] values)
{
List<FormulaOp> ops = new List<FormulaOp>();
string[] tokens = formula.Split(' ');
foreach(string token in tokens)
{
string[] param = token.Split('=');

if(param.Length == 1)
continue;

int res = WhichVariable(param[0][0]);
if(char.IsDigit(param[1][0]))
values[res] = Convert.ToUInt32(param[1]);
else
{
string method = param[1];
ops.Add(new FormulaOp(method[1], res, WhichVariable(method[0]), WhichVariable(method[2])));
}
}
return ops;
}
private static int WhichVariable(char param)
{
int res = (param) - 'A';
if(res > 2) res = 3;
return res;
}

private class FormulaOp
{
public int Variable1 { get; private set; }
public int Variable2 { get; private set; }
public int Result { get; private set; }
public char Operation { get; private set; }
public FormulaOp(char op, int result, int variable1, int variable2)
{
Result = result; Variable1 = variable1; Variable2 = variable2; Operation = op;
}
}
}
}


So how fast is it? With a cold cache, it executes in ~40ms on average. With a warm cache, however, that number drops to a near constant 16ms. I'm pretty sure that's the lower bound for System.Diagnostics.Stopwatch, so it's probably a bit faster than that even. And I could speed this up by p/invoking MapViewOfFile and friends, I just don't think it's worth the effort at this point. The code necessary to wrap the native MapViewOfFile functions is out there, however. I could probably do better by building the formula a bit cleaner too, and by not passing a FileStream and passing the filename directly instead (less property accesses and whatnot), but meh. Also note that this code is slightly more direct and slightly smaller than what the compiler will produce if you were to write the formula in code as a plain C# function. It uses a few more ops and another label to have the condition test at the bottom of the loop and uses a different branch (brtrue instead of blt). Since I know the file will never be null at the start, I can simply go directly into the loop.

Also: If you look closely, you can see bits of MBNCSUtil's CheckRevision formula builder in there. I used MBNCSUtil as a reference when implementing my own formula builder, but then ended up rewriting a good portion of the code anyway, since my initial attempt was fairly broken.